{"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":"# loss_history","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import datetime\nimport os\n\nimport torch\nimport matplotlib\nmatplotlib.use('Agg')\nimport scipy.signal\nfrom matplotlib import pyplot as plt\nfrom torch.utils.tensorboard import SummaryWriter\n\n\nclass LossHistory():\n    def __init__(self, log_dir, model, input_shape):\n        time_str        = datetime.datetime.strftime(datetime.datetime.now(),'%Y_%m_%d_%H_%M_%S')\n        self.log_dir    = os.path.join(log_dir, \"loss_\" + str(time_str))\n        self.losses     = []\n        self.val_loss   = []\n        \n        os.makedirs(self.log_dir)\n        self.writer     = SummaryWriter(self.log_dir)\n        try:\n            dummy_input     = torch.randn(2, 3, input_shape[0], input_shape[1])\n            self.writer.add_graph(model, dummy_input)\n        except:\n            pass\n\n    def append_loss(self, epoch, loss, val_loss):\n        if not os.path.exists(self.log_dir):\n            os.makedirs(self.log_dir)\n\n        self.losses.append(loss)\n        self.val_loss.append(val_loss)\n\n        with open(os.path.join(self.log_dir, \"epoch_loss.txt\"), 'a') as f:\n            f.write(str(loss))\n            f.write(\"\\n\")\n        with open(os.path.join(self.log_dir, \"epoch_val_loss.txt\"), 'a') as f:\n            f.write(str(val_loss))\n            f.write(\"\\n\")\n\n        self.writer.add_scalar('loss', loss, epoch)\n        self.writer.add_scalar('val_loss', val_loss, epoch)\n        self.loss_plot()\n\n    def loss_plot(self):\n        iters = range(len(self.losses))\n\n        plt.figure()\n        plt.plot(iters, self.losses, 'red', linewidth = 2, label='labels loss')\n        plt.plot(iters, self.val_loss, 'coral', linewidth = 2, label='val loss')\n        try:\n            if len(self.losses) < 25:\n                num = 5\n            else:\n                num = 15\n            \n            plt.plot(iters, scipy.signal.savgol_filter(self.losses, num, 3), 'green', linestyle = '--', linewidth = 2, label='smooth labels loss')\n            plt.plot(iters, scipy.signal.savgol_filter(self.val_loss, num, 3), '#8B4513', linestyle = '--', linewidth = 2, label='smooth val loss')\n        except:\n            pass\n\n        plt.grid(True)\n        plt.xlabel('Epoch')\n        plt.ylabel('Loss')\n        plt.legend(loc=\"upper right\")\n\n        plt.savefig(os.path.join(self.log_dir, \"epoch_loss.png\"))\n\n        plt.cla()\n        plt.close(\"all\")","metadata":{"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%cd /kaggle/input/final-1/yolo3-pytorch-master\n# %cd /kaggle/input/weights\n# !ls","metadata":{"execution":{"iopub.status.busy":"2022-04-30T05:59:03.147094Z","iopub.execute_input":"2022-04-30T05:59:03.147335Z","iopub.status.idle":"2022-04-30T05:59:03.15598Z","shell.execute_reply.started":"2022-04-30T05:59:03.147299Z","shell.execute_reply":"2022-04-30T05:59:03.155307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# TRAIN","metadata":{}},{"cell_type":"code","source":"#-------------------------------------#\n#       对数据集进行训练\n#-------------------------------------#\nimport numpy as np\nimport torch\nimport torch.backends.cudnn as cudnn\nimport torch.optim as optim\nfrom torch import nn\nfrom torch.utils.data import DataLoader\n\nfrom nets.yolo import YoloBody\nfrom nets.yolo_training import (YOLOLoss, get_lr_scheduler, set_optimizer_lr,\n                                weights_init)\n\nfrom utils.dataloader import YoloDataset, yolo_dataset_collate\nfrom utils.utils import get_anchors, get_classes\nfrom utils.utils_fit import fit_one_epoch\nimport sys\nsys.path.append(\".\")\n\n'''\n训练自己的目标检测模型一定需要注意以下几点：\n1、训练前仔细检查自己的格式是否满足要求，该库要求数据集格式为VOC格式，需要准备好的内容有输入图片和标签\n   输入图片为.jpg图片，无需固定大小，传入训练前会自动进行resize。\n   灰度图会自动转成RGB图片进行训练，无需自己修改。\n   输入图片如果后缀非jpg，需要自己批量转成jpg后再开始训练。\n\n   标签为.xml格式，文件中会有需要检测的目标信息，标签文件和输入图片文件相对应。\n\n2、训练好的权值文件保存在logs文件夹中，每个epoch都会保存一次，如果只是训练了几个step是不会保存的，epoch和step的概念要捋清楚一下。\n   在训练过程中，该代码并没有设定只保存最低损失的，因此按默认参数训练完会有100个权值，如果空间不够可以自行删除。\n   这个并不是保存越少越好也不是保存越多越好，有人想要都保存、有人想只保存一点，为了满足大多数的需求，还是都保存可选择性高。\n\n3、损失值的大小用于判断是否收敛，比较重要的是有收敛的趋势，即验证集损失不断下降，如果验证集损失基本上不改变的话，模型基本上就收敛了。\n   损失值的具体大小并没有什么意义，大和小只在于损失的计算方式，并不是接近于0才好。如果想要让损失好看点，可以直接到对应的损失函数里面除上10000。\n   训练过程中的损失值会保存在logs文件夹下的loss_%Y_%m_%d_%H_%M_%S文件夹中\n\n4、调参是一门蛮重要的学问，没有什么参数是一定好的，现有的参数是我测试过可以正常训练的参数，因此我会建议用现有的参数。\n   但是参数本身并不是绝对的，比如随着batch的增大学习率也可以增大，效果也会好一些；过深的网络不要用太大的学习率等等。\n   这些都是经验上，只能靠各位同学多查询资料和自己试试了。\n'''  \nif __name__ == \"__main__\":\n    #-------------------------------#\n    #   是否使用Cuda\n    #   没有GPU可以设置成False\n    #-------------------------------#\n    Cuda            = True\n    #---------------------------------------------------------------------#\n    #   classes_path    指向model_data下的txt，与自己训练的数据集相关 \n    #                   训练前一定要修改classes_path，使其对应自己的数据集\n    #---------------------------------------------------------------------#\n    classes_path    = 'model_data/my_classes.txt'\n    #---------------------------------------------------------------------#\n    #   anchors_path    代表先验框对应的txt文件，一般不修改。\n    #   anchors_mask    用于帮助代码找到对应的先验框，一般不修改。\n    #---------------------------------------------------------------------#\n    anchors_path    = '/kaggle/input/weights/yolo_anchors.txt'\n    anchors_mask    = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]\n    #----------------------------------------------------------------------------------------------------------------------------#\n    #   权值文件的下载请看README，可以通过网盘下载。模型的 预训练权重 对不同数据集是通用的，因为特征是通用的。\n    #   模型的 预训练权重 比较重要的部分是 主干特征提取网络的权值部分，用于进行特征提取。\n    #   预训练权重对于99%的情况都必须要用，不用的话主干部分的权值太过随机，特征提取效果不明显，网络训练的结果也不会好\n    #\n    #   如果训练过程中存在中断训练的操作，可以将model_path设置成logs文件夹下的权值文件，将已经训练了一部分的权值再次载入。\n    #   同时修改下方的 冻结阶段 或者 解冻阶段 的参数，来保证模型epoch的连续性。\n    #   \n    #   当model_path = ''的时候不加载整个模型的权值。\n    #\n    #   此处使用的是整个模型的权重，因此是在train.py进行加载的，下面的pretrain不影响此处的权值加载。\n    #   如果想要让模型从主干的预训练权值开始训练，则设置model_path = ''，下面的pretrain = True，此时仅加载主干。\n    #   如果想要让模型从0开始训练，则设置model_path = ''，下面的pretrain = Fasle，Freeze_Train = Fasle，此时从0开始训练，且没有冻结主干的过程。\n    #   \n    #   一般来讲，网络从0开始的训练效果会很差，因为权值太过随机，特征提取效果不明显，因此非常、非常、非常不建议大家从0开始训练！\n    #   如果一定要从0开始，可以了解imagenet数据集，首先训练分类模型，获得网络的主干部分权值，分类模型的 主干部分 和该模型通用，基于此进行训练。\n    #----------------------------------------------------------------------------------------------------------------------------#\n    model_path      = '/kaggle/input/weights/ep200-loss0.057-val_loss0.054.pth'\n    #------------------------------------------------------#\n    #   input_shape     输入的shape大小，一定要是32的倍数\n    #------------------------------------------------------#\n    input_shape     = [640,640]\n    #----------------------------------------------------------------------------------------------------------------------------#\n    #   pretrained      是否使用主干网络的预训练权重，此处使用的是主干的权重，因此是在模型构建的时候进行加载的。\n    #                   如果设置了model_path，则主干的权值无需加载，pretrained的值无意义。\n    #                   如果不设置model_path，pretrained = True，此时仅加载主干开始训练。\n    #                   如果不设置model_path，pretrained = False，Freeze_Train = Fasle，此时从0开始训练，且没有冻结主干的过程。\n    #----------------------------------------------------------------------------------------------------------------------------#\n    pretrained      = True\n    \n    #----------------------------------------------------------------------------------------------------------------------------#\n    #   训练分为两个阶段，分别是冻结阶段和解冻阶段。设置冻结阶段是为了满足机器性能不足的同学的训练需求。\n    #   冻结训练需要的显存较小，显卡非常差的情况下，可设置Freeze_Epoch等于UnFreeze_Epoch，此时仅仅进行冻结训练。\n    #      \n    #   在此提供若干参数设置建议，各位训练者根据自己的需求进行灵活调整：\n    #   （一）从整个模型的预训练权重开始训练： \n    #       Init_Epoch = 0，Freeze_Epoch = 50，UnFreeze_Epoch = 100，Freeze_Train = True（默认参数）\n    #       Init_Epoch = 0，UnFreeze_Epoch = 100，Freeze_Train = False（不冻结训练）\n    #       其中：UnFreeze_Epoch可以在100-300之间调整。optimizer_type = 'sgd'，Init_lr = 1e-2。\n    #   （二）从主干网络的预训练权重开始训练：\n    #       Init_Epoch = 0，Freeze_Epoch = 50，UnFreeze_Epoch = 300，Freeze_Train = True（冻结训练）\n    #       Init_Epoch = 0，UnFreeze_Epoch = 300，Freeze_Train = False（不冻结训练）\n    #       其中：由于从主干网络的预训练权重开始训练，主干的权值不一定适合目标检测，需要更多的训练跳出局部最优解。\n    #             UnFreeze_Epoch可以在200-300之间调整，YOLOV5和YOLOX均推荐使用300。optimizer_type = 'sgd'，Init_lr = 1e-2。\n    #   （三）batch_size的设置：\n    #       在显卡能够接受的范围内，以大为好。显存不足与数据集大小无关，提示显存不足（OOM或者CUDA out of memory）请调小batch_size。\n    #       受到BatchNorm层影响，batch_size最小为2，不能为1。\n    #       正常情况下Freeze_batch_size建议为Unfreeze_batch_size的1-2倍。不建议设置的差距过大，因为关系到学习率的自动调整。\n    #----------------------------------------------------------------------------------------------------------------------------#\n    #------------------------------------------------------------------#\n    #   冻结阶段训练参数\n    #   此时模型的主干被冻结了，特征提取网络不发生改变\n    #   占用的显存较小，仅对网络进行微调\n    #   Init_Epoch          模型当前开始的训练世代，其值可以大于Freeze_Epoch，如设置：\n    #                       Init_Epoch = 60、Freeze_Epoch = 50、UnFreeze_Epoch = 100\n    #                       会跳过冻结阶段，直接从60代开始，并调整对应的学习率。\n    #                       （断点续练时使用）\n    #   Freeze_Epoch        模型冻结训练的Freeze_Epoch\n    #                       (当Freeze_Train=False时失效)\n    #   Freeze_batch_size   模型冻结训练的batch_size\n    #                       (当Freeze_Train=False时失效)\n    #------------------------------------------------------------------#\n    Init_Epoch          = 0\n    Freeze_Epoch        = 0\n    Freeze_batch_size   = 8\n    #------------------------------------------------------------------#\n    #   解冻阶段训练参数\n    #   此时模型的主干不被冻结了，特征提取网络会发生改变\n    #   占用的显存较大，网络所有的参数都会发生改变\n    #   UnFreeze_Epoch          模型总共训练的epoch\n    #   Unfreeze_batch_size     模型在解冻后的batch_size\n    #------------------------------------------------------------------#\n    UnFreeze_Epoch      = 1000\n    Unfreeze_batch_size = 8\n    #------------------------------------------------------------------#\n    #   Freeze_Train    是否进行冻结训练\n    #                   默认先冻结主干训练后解冻训练。\n    #------------------------------------------------------------------#\n    Freeze_Train        = True\n\n    #------------------------------------------------------------------#\n    #   其它训练参数：学习率、优化器、学习率下降有关\n    #------------------------------------------------------------------#\n    #------------------------------------------------------------------#\n    #   Init_lr         模型的最大学习率\n    #   Min_lr          模型的最小学习率，默认为最大学习率的0.01\n    #------------------------------------------------------------------#\n    Init_lr             = 1e-2\n    Min_lr              = Init_lr * 0.01\n    #------------------------------------------------------------------#\n    #   optimizer_type  使用到的优化器种类，可选的有adam、sgd\n    #                   当使用Adam优化器时建议设置  Init_lr=1e-3\n    #                   当使用SGD优化器时建议设置   Init_lr=1e-2\n    #   momentum        优化器内部使用到的momentum参数\n    #   weight_decay    权值衰减，可防止过拟合\n    #------------------------------------------------------------------#\n    optimizer_type      = \"sgd\"\n    momentum            = 0.937\n    weight_decay        = 5e-4\n    #------------------------------------------------------------------#\n    #   lr_decay_type   使用到的学习率下降方式，可选的有step、cos\n    #------------------------------------------------------------------#\n    lr_decay_type       = \"cos\"\n    #------------------------------------------------------------------#\n    #   save_period     多少个epoch保存一次权值，默认每个世代都保存\n    #------------------------------------------------------------------#\n    save_period         = 1\n    #------------------------------------------------------------------#\n    #   save_dir        权值与日志文件保存的文件夹\n    #------------------------------------------------------------------#\n    save_dir            = '/kaggle/working/logs'\n    #------------------------------------------------------------------#\n    #   num_workers     用于设置是否使用多线程读取数据\n    #                   开启后会加快数据读取速度，但是会占用更多内存\n    #                   内存较小的电脑可以设置为2或者0  \n    #------------------------------------------------------------------#\n    num_workers         = 0\n\n    #----------------------------------------------------#\n    #   获得图片路径和标签\n    #----------------------------------------------------#\n    train_annotation_path   = '/kaggle/input/final1txt/2007_train.txt'\n    val_annotation_path     = '/kaggle/input/final1txt/2007_val.txt'\n\n    #----------------------------------------------------#\n    #   获取classes和anchor\n    #----------------------------------------------------#\n    class_names, num_classes = get_classes(classes_path)\n    anchors, num_anchors     = get_anchors(anchors_path)\n\n    #------------------------------------------------------#\n    #   创建yolo模型\n    #------------------------------------------------------#\n    model = YoloBody(anchors_mask, num_classes, pretrained=pretrained)\n    if not pretrained:\n        weights_init(model)\n    if model_path != '':\n        #------------------------------------------------------#\n        #   权值文件请看README，百度网盘下载\n        #------------------------------------------------------#\n        print('Load weights {}.'.format(model_path))\n        device          = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n        model_dict      = model.state_dict()\n        pretrained_dict = torch.load(model_path, map_location = device)\n        pretrained_dict = {k: v for k, v in pretrained_dict.items() if np.shape(model_dict[k]) == np.shape(v)}\n        model_dict.update(pretrained_dict)\n        model.load_state_dict(model_dict)\n\n    yolo_loss    = YOLOLoss(anchors, num_classes, input_shape, Cuda, anchors_mask)\n    loss_history = LossHistory(save_dir, model, input_shape=input_shape)\n\n    model_train = model.train()\n    if Cuda:\n        model_train = torch.nn.DataParallel(model)\n        cudnn.benchmark = True\n        model_train = model_train.cuda()\n\n    #---------------------------#\n    #   读取数据集对应的txt\n    #---------------------------#\n    with open(train_annotation_path) as f:\n        train_lines = f.readlines()\n    with open(val_annotation_path) as f:\n        val_lines   = f.readlines()\n    num_train   = len(train_lines)\n    num_val     = len(val_lines)\n\n    #------------------------------------------------------#\n    #   主干特征提取网络特征通用，冻结训练可以加快训练速度\n    #   也可以在训练初期防止权值被破坏。\n    #   Init_Epoch为起始世代\n    #   Freeze_Epoch为冻结训练的世代\n    #   UnFreeze_Epoch总训练世代\n    #   提示OOM或者显存不足请调小Batch_size\n    #------------------------------------------------------#\n    if True:\n        UnFreeze_flag = False\n        #------------------------------------#\n        #   冻结一定部分训练\n        #------------------------------------#\n        if Freeze_Train:\n            for param in model.backbone.parameters():\n                param.requires_grad = False\n\n        #-------------------------------------------------------------------#\n        #   如果不冻结训练的话，直接设置batch_size为Unfreeze_batch_size\n        #-------------------------------------------------------------------#\n        batch_size = Freeze_batch_size if Freeze_Train else Unfreeze_batch_size\n\n        #-------------------------------------------------------------------#\n        #   判断当前batch_size与64的差别，自适应调整学习率\n        #-------------------------------------------------------------------#\n        nbs         = 64\n        Init_lr_fit = max(batch_size / nbs * Init_lr, 1e-4)\n        Min_lr_fit  = max(batch_size / nbs * Min_lr, 1e-6)\n\n        #---------------------------------------#\n        #   根据optimizer_type选择优化器\n        #---------------------------------------#\n        pg0, pg1, pg2 = [], [], []  \n        for k, v in model.named_modules():\n            if hasattr(v, \"bias\") and isinstance(v.bias, nn.Parameter):\n                pg2.append(v.bias)    \n            if isinstance(v, nn.BatchNorm2d) or \"bn\" in k:\n                pg0.append(v.weight)    \n            elif hasattr(v, \"weight\") and isinstance(v.weight, nn.Parameter):\n                pg1.append(v.weight)   \n        optimizer = {\n            'adam'  : optim.Adam(pg0, Init_lr_fit, betas = (momentum, 0.999)),\n            'sgd'   : optim.SGD(pg0, Init_lr_fit, momentum = momentum, nesterov=True)\n        }[optimizer_type]\n        optimizer.add_param_group({\"params\": pg1, \"weight_decay\": weight_decay})\n        optimizer.add_param_group({\"params\": pg2})\n\n        #---------------------------------------#\n        #   获得学习率下降的公式\n        #---------------------------------------#\n        lr_scheduler_func = get_lr_scheduler(lr_decay_type, Init_lr_fit, Min_lr_fit, UnFreeze_Epoch)\n        \n        #---------------------------------------#\n        #   判断每一个世代的长度\n        #---------------------------------------#\n        epoch_step      = num_train // batch_size\n        epoch_step_val  = num_val // batch_size\n        \n        if epoch_step == 0 or epoch_step_val == 0:\n            raise ValueError(\"数据集过小，无法继续进行训练，请扩充数据集。\")\n\n        #---------------------------------------#\n        #   构建数据集加载器。\n        #---------------------------------------#\n        train_dataset   = YoloDataset(train_lines, input_shape, num_classes, train = True)\n        val_dataset     = YoloDataset(val_lines, input_shape, num_classes, train = False)\n        gen             = DataLoader(train_dataset, shuffle = True, batch_size = batch_size, num_workers = num_workers, pin_memory=True,\n                                    drop_last=True, collate_fn=yolo_dataset_collate)\n        gen_val         = DataLoader(val_dataset  , shuffle = True, batch_size = batch_size, num_workers = num_workers, pin_memory=True, \n                                    drop_last=True, collate_fn=yolo_dataset_collate)\n\n        #---------------------------------------#\n        #   开始模型训练\n        #---------------------------------------#\n        for epoch in range(Init_Epoch, UnFreeze_Epoch):\n            #---------------------------------------#\n            #   如果模型有冻结学习部分\n            #   则解冻，并设置参数\n            #---------------------------------------#\n            if epoch >= Freeze_Epoch and not UnFreeze_flag and Freeze_Train:\n                batch_size = Unfreeze_batch_size\n\n                #-------------------------------------------------------------------#\n                #   判断当前batch_size与64的差别，自适应调整学习率\n                #-------------------------------------------------------------------#\n                nbs         = 64\n                Init_lr_fit = max(batch_size / nbs * Init_lr, 1e-4)\n                Min_lr_fit  = max(batch_size / nbs * Min_lr, 1e-6)\n                #---------------------------------------#\n                #   获得学习率下降的公式\n                #---------------------------------------#\n                lr_scheduler_func = get_lr_scheduler(lr_decay_type, Init_lr_fit, Min_lr_fit, UnFreeze_Epoch)\n                \n                for param in model.backbone.parameters():\n                    param.requires_grad = True\n\n                epoch_step      = num_train // batch_size\n                epoch_step_val  = num_val // batch_size\n\n                if epoch_step == 0 or epoch_step_val == 0:\n                    raise ValueError(\"数据集过小，无法继续进行训练，请扩充数据集。\")\n\n                gen     = DataLoader(train_dataset, shuffle = True, batch_size = batch_size, num_workers = num_workers, pin_memory=True,\n                                            drop_last=True, collate_fn=yolo_dataset_collate)\n                gen_val = DataLoader(val_dataset  , shuffle = True, batch_size = batch_size, num_workers = num_workers, pin_memory=True, \n                                            drop_last=True, collate_fn=yolo_dataset_collate)\n\n                UnFreeze_flag = True\n                \n            set_optimizer_lr(optimizer, lr_scheduler_func, epoch)\n\n            fit_one_epoch(model_train, model, yolo_loss, loss_history, optimizer, epoch, epoch_step, epoch_step_val, gen, gen_val, UnFreeze_Epoch, Cuda, save_period, save_dir)\n\n        loss_history.writer.close()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-04-30T06:00:34.566896Z","iopub.execute_input":"2022-04-30T06:00:34.567163Z","iopub.status.idle":"2022-04-30T06:21:32.394416Z","shell.execute_reply.started":"2022-04-30T06:00:34.567134Z","shell.execute_reply":"2022-04-30T06:21:32.393344Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# net.yolo","metadata":{}},{"cell_type":"code","source":"from collections import OrderedDict\n\nimport torch\nimport torch.nn as nn\n\nfrom nets.darknet import darknet53\n# from nets.Own_ResNet import resnet50\nfrom nets.resnet import resnet50\n\ndef conv2d(filter_in, filter_out, kernel_size):\n    pad = (kernel_size - 1) // 2 if kernel_size else 0\n    return nn.Sequential(OrderedDict([\n        (\"conv\", nn.Conv2d(filter_in, filter_out, kernel_size=kernel_size, stride=1, padding=pad, bias=False)),\n        (\"bn\", nn.BatchNorm2d(filter_out)),\n        (\"relu\", nn.LeakyReLU(0.1)),\n    ]))\n\n#------------------------------------------------------------------------#\n#   make_last_layers里面一共有七个卷积，前五个用于提取特征。\n#   后两个用于获得yolo网络的预测结果\n#------------------------------------------------------------------------#\ndef make_last_layers(filters_list, in_filters, out_filter):\n    m = nn.Sequential(\n        conv2d(in_filters, filters_list[0], 1),\n        conv2d(filters_list[0], filters_list[1], 3),\n        conv2d(filters_list[1], filters_list[0], 1),\n        conv2d(filters_list[0], filters_list[1], 3),\n        conv2d(filters_list[1], filters_list[0], 1),\n        conv2d(filters_list[0], filters_list[1], 3),\n        nn.Conv2d(filters_list[1], out_filter, kernel_size=1, stride=1, padding=0, bias=True)\n    )\n    return m\n\nclass YoloBody(nn.Module):\n    def __init__(self, anchors_mask, num_classes, pretrained = False):\n        super(YoloBody, self).__init__()\n        #---------------------------------------------------#   \n        #   生成darknet53的主干模型(改为resnet50)\n        #   获得三个有效特征层，他们的shape分别是：\n        #   52,52,256\n        #   26,26,512\n        #   13,13,1024\n        #---------------------------------------------------#\n        self.backbone = resnet50()\n        if pretrained:\n            self.backbone.load_state_dict(torch.load(\"model_data/resnet50-19c8e357.pth\"),False)\n\n        #---------------------------------------------------#\n        #   out_filters : [64, 128, 256, 512, 1024]\n        #---------------------------------------------------#\n        out_filters = self.backbone.layers_out_filters\n\n        #------------------------------------------------------------------------#\n        #   计算yolo_head的输出通道数，对于voc数据集而言\n        #   final_out_filter0 = final_out_filter1 = final_out_filter2 = 75\n        #   笔记：\n        #       以下为其输出的三层特征\n        #------------------------------------------------------------------------#\n        self.last_layer0            = make_last_layers([512, 1024], out_filters[-1], len(anchors_mask[0]) * (num_classes + 5))\n\n        self.last_layer1_conv       = conv2d(512, 256, 1)\n        self.last_layer1_upsample   = nn.Upsample(scale_factor=2, mode='nearest')\n        self.last_layer1            = make_last_layers([256, 512], out_filters[-2] + 256, len(anchors_mask[1]) * (num_classes + 5))\n\n        self.last_layer2_conv       = conv2d(256, 128, 1)\n        self.last_layer2_upsample   = nn.Upsample(scale_factor=2, mode='nearest')\n        self.last_layer2            = make_last_layers([128, 256], out_filters[-3] + 128, len(anchors_mask[2]) * (num_classes + 5))\n        \n#         self.last_layer3_conv       = conv2d(128, 64, 1)\n#         self.last_layer3_upsample   = nn.Upsample(scale_factor=2, mode='nearest')\n#         self.last_layer3            = make_last_layers([64, 128], out_filters[-3] + 128, len(anchors_mask[3]) * (num_classes + 5))\n\n    def forward(self, x):\n        #---------------------------------------------------#   \n        #   获得三个有效特征层，他们的shape分别是：\n        #   52,52,256；26,26,512；13,13,1024\n        #---------------------------------------------------#\n        x2, x1, x0 = self.backbone(x)\n\n        #---------------------------------------------------#\n        #   第一个特征层\n        #   out0 = (batch_size,255,13,13)\n        #---------------------------------------------------#\n        # 13,13,1024 -> 13,13,512 -> 13,13,1024 -> 13,13,512 -> 13,13,1024 -> 13,13,512\n        out0_branch = self.last_layer0[:5](x0)\n        out0        = self.last_layer0[5:](out0_branch)\n\n        # 13,13,512 -> 13,13,256 -> 26,26,256\n        x1_in = self.last_layer1_conv(out0_branch)\n        x1_in = self.last_layer1_upsample(x1_in)\n\n        # 26,26,256 + 26,26,512 -> 26,26,768\n        x1_in = torch.cat([x1_in, x1], 1)\n        #---------------------------------------------------#\n        #   第二个特征层\n        #   out1 = (batch_size,255,26,26)\n        #---------------------------------------------------#\n        # 26,26,768 -> 26,26,256 -> 26,26,512 -> 26,26,256 -> 26,26,512 -> 26,26,256\n        out1_branch = self.last_layer1[:5](x1_in)\n        out1        = self.last_layer1[5:](out1_branch)\n\n        # 26,26,256 -> 26,26,128 -> 52,52,128\n        x2_in = self.last_layer2_conv(out1_branch)\n        x2_in = self.last_layer2_upsample(x2_in)\n\n        # 52,52,128 + 52,52,256 -> 52,52,384\n        x2_in = torch.cat([x2_in, x2], 1)\n        #---------------------------------------------------#\n        #   第三个特征层\n        #   out3 = (batch_size,255,52,52)\n        #---------------------------------------------------#\n        # 52,52,384 -> 52,52,128 -> 52,52,256 -> 52,52,128 -> 52,52,256 -> 52,52,128\n        out2 = self.last_layer2(x2_in)\n        return out0, out1, out2","metadata":{"execution":{"iopub.status.busy":"2022-04-30T06:23:14.748437Z","iopub.execute_input":"2022-04-30T06:23:14.748884Z","iopub.status.idle":"2022-04-30T06:23:14.768726Z","shell.execute_reply.started":"2022-04-30T06:23:14.748851Z","shell.execute_reply":"2022-04-30T06:23:14.767956Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# YOLO","metadata":{}},{"cell_type":"code","source":"import colorsys\nimport os\nimport time\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom PIL import ImageDraw, ImageFont\n\nfrom utils.utils import (cvtColor, get_anchors, get_classes, preprocess_input,\n                         resize_image)\nfrom utils.utils_bbox import DecodeBox\n\n'''\n训练自己的数据集必看注释！\n'''\nclass YOLO(object):\n    _defaults = {\n        #--------------------------------------------------------------------------#\n        #   使用自己训练好的模型进行预测一定要修改model_path和classes_path！\n        #   model_path指向logs文件夹下的权值文件，classes_path指向model_data下的txt\n        #\n        #   训练好后logs文件夹下存在多个权值文件，选择验证集损失较低的即可。\n        #   验证集损失较低不代表mAP较高，仅代表该权值在验证集上泛化性能较好。\n        #   如果出现shape不匹配，同时要注意训练时的model_path和classes_path参数的修改\n        #--------------------------------------------------------------------------#\n        \"model_path\"        : '/kaggle/input/weights/ep200-loss0.057-val_loss0.054.pth',\n        \"classes_path\"      : 'model_data/my_classes.txt',\n        #---------------------------------------------------------------------#\n        #   anchors_path代表先验框对应的txt文件，一般不修改。\n        #   anchors_mask用于帮助代码找到对应的先验框，一般不修改。\n        #---------------------------------------------------------------------#\n        \"anchors_path\"      : '/kaggle/input/weights/yolo_anchors.txt',\n        \"anchors_mask\"      : [[6, 7, 8], [3, 4, 5], [0, 1, 2]],\n        #---------------------------------------------------------------------#\n        #   输入图片的大小，必须为32的倍数。[416, 416]\n        #---------------------------------------------------------------------#\n        \"input_shape\"       : [640,640],\n        #---------------------------------------------------------------------#\n        #   只有得分大于置信度的预测框会被保留下来 0.5\n        #---------------------------------------------------------------------#\n        \"confidence\"        : 0.5,\n        #---------------------------------------------------------------------#\n        #   非极大抑制所用到的nms_iou大小\n        #---------------------------------------------------------------------#\n        \"nms_iou\"           : 0.9,\n        #---------------------------------------------------------------------#\n        #   该变量用于控制是否使用letterbox_image对输入图像进行不失真的resize，\n        #   在多次测试后，发现关闭letterbox_image直接resize的效果更好\n        #---------------------------------------------------------------------#\n        \"letterbox_image\"   : False,\n        #-------------------------------#\n        #   是否使用Cuda\n        #   没有GPU可以设置成False\n        #-------------------------------#\n        \"cuda\"              : True,\n    }\n\n    @classmethod\n    def get_defaults(cls, n):\n        if n in cls._defaults:\n            return cls._defaults[n]\n        else:\n            return \"Unrecognized attribute name '\" + n + \"'\"\n\n    #---------------------------------------------------#\n    #   初始化YOLO\n    #---------------------------------------------------#\n    def __init__(self, **kwargs):\n        self.__dict__.update(self._defaults)\n        for name, value in kwargs.items():\n            setattr(self, name, value)\n            \n        #---------------------------------------------------#\n        #   获得种类和先验框的数量\n        #---------------------------------------------------#\n        self.class_names, self.num_classes  = get_classes(self.classes_path)\n        self.anchors, self.num_anchors      = get_anchors(self.anchors_path)\n        self.bbox_util                      = DecodeBox(self.anchors, self.num_classes, (self.input_shape[0], self.input_shape[1]), self.anchors_mask)\n\n        #---------------------------------------------------#\n        #   画框设置不同的颜色\n        #---------------------------------------------------#\n        hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)]\n        self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))\n        self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors))\n        self.generate()\n\n    #---------------------------------------------------#\n    #   生成模型\n    #---------------------------------------------------#\n    def generate(self):\n        #---------------------------------------------------#\n        #   建立yolov3模型，载入yolov3模型的权重\n        #---------------------------------------------------#\n        self.net    = YoloBody(self.anchors_mask, self.num_classes)\n        device      = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n        self.net.load_state_dict(torch.load(self.model_path, map_location=device))\n        self.net    = self.net.eval()\n        print('{} model, anchors, and classes loaded.'.format(self.model_path))\n\n        if self.cuda:\n            self.net = nn.DataParallel(self.net)\n            self.net = self.net.cuda()\n\n    #---------------------------------------------------#\n    #   检测图片\n    #---------------------------------------------------#\n    def detect_image(self, image, crop = False):\n        image_shape = np.array(np.shape(image)[0:2])\n        #---------------------------------------------------------#\n        #   在这里将图像转换成RGB图像，防止灰度图在预测时报错。\n        #   代码仅仅支持RGB图像的预测，所有其它类型的图像都会转化成RGB\n        #---------------------------------------------------------#\n        image       = cvtColor(image)\n        #---------------------------------------------------------#\n        #   给图像增加灰条，实现不失真的resize\n        #   也可以直接resize进行识别\n        #---------------------------------------------------------#\n        image_data  = resize_image(image, (self.input_shape[1],self.input_shape[0]), self.letterbox_image)\n        #---------------------------------------------------------#\n        #   添加上batch_size维度\n        #---------------------------------------------------------#\n        image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0)\n\n        with torch.no_grad():\n            images = torch.from_numpy(image_data)\n            if self.cuda:\n                images = images.cuda()\n            #---------------------------------------------------------#\n            #   将图像输入网络当中进行预测！\n            #---------------------------------------------------------#\n            outputs = self.net(images)\n            outputs = self.bbox_util.decode_box(outputs)\n            #---------------------------------------------------------#\n            #   将预测框进行堆叠，然后进行非极大抑制\n            #---------------------------------------------------------#\n            results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape, \n                        image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou)\n                                                    \n            if results[0] is None: \n                return image\n\n            top_label   = np.array(results[0][:, 6], dtype = 'int32')\n            top_conf    = results[0][:, 4] * results[0][:, 5]\n            top_boxes   = results[0][:, :4]\n        #---------------------------------------------------------#\n        #   设置字体与边框厚度\n        #---------------------------------------------------------#\n        font        = ImageFont.truetype(font='model_data/simhei.ttf', size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))\n        thickness   = int(max((image.size[0] + image.size[1]) // np.mean(self.input_shape), 1))\n        \n        #---------------------------------------------------------#\n        #   是否进行目标的裁剪\n        #---------------------------------------------------------#\n        if crop:\n            for i, c in list(enumerate(top_label)):\n                top, left, bottom, right = top_boxes[i]\n                top     = max(0, np.floor(top).astype('int32'))\n                left    = max(0, np.floor(left).astype('int32'))\n                bottom  = min(image.size[1], np.floor(bottom).astype('int32'))\n                right   = min(image.size[0], np.floor(right).astype('int32'))\n                \n                dir_save_path = \"img_crop\"\n                if not os.path.exists(dir_save_path):\n                    os.makedirs(dir_save_path)\n                crop_image = image.crop([left, top, right, bottom])\n                crop_image.save(os.path.join(dir_save_path, \"crop_\" + str(i) + \".png\"), quality=95, subsampling=0)\n                print(\"save crop_\" + str(i) + \".png to \" + dir_save_path)\n        #---------------------------------------------------------#\n        #   图像绘制\n        #---------------------------------------------------------#\n        for i, c in list(enumerate(top_label)):\n            predicted_class = self.class_names[int(c)]\n            box             = top_boxes[i]\n            score           = top_conf[i]\n\n            top, left, bottom, right = box\n\n            top     = max(0, np.floor(top).astype('int32'))\n            left    = max(0, np.floor(left).astype('int32'))\n            bottom  = min(image.size[1], np.floor(bottom).astype('int32'))\n            right   = min(image.size[0], np.floor(right).astype('int32'))\n\n            label = '{} {:.2f}'.format(predicted_class, score)\n            draw = ImageDraw.Draw(image)\n            label_size = draw.textsize(label, font)\n            label = label.encode('utf-8')\n            print(label, top, left, bottom, right)\n            \n            if top - label_size[1] >= 0:\n                text_origin = np.array([left, top - label_size[1]])\n            else:\n                text_origin = np.array([left, top + 1])\n\n            for i in range(thickness):\n                draw.rectangle([left + i, top + i, right - i, bottom - i], outline=self.colors[c])\n            draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)], fill=self.colors[c])\n            draw.text(text_origin, str(label,'UTF-8'), fill=(0, 0, 0), font=font)\n            del draw\n\n        return image\n\n    def get_FPS(self, image, test_interval):\n        image_shape = np.array(np.shape(image)[0:2])\n        #---------------------------------------------------------#\n        #   在这里将图像转换成RGB图像，防止灰度图在预测时报错。\n        #   代码仅仅支持RGB图像的预测，所有其它类型的图像都会转化成RGB\n        #---------------------------------------------------------#\n        image       = cvtColor(image)\n        #---------------------------------------------------------#\n        #   给图像增加灰条，实现不失真的resize\n        #   也可以直接resize进行识别\n        #---------------------------------------------------------#\n        image_data  = resize_image(image, (self.input_shape[1],self.input_shape[0]), self.letterbox_image)\n        #---------------------------------------------------------#\n        #   添加上batch_size维度\n        #---------------------------------------------------------#\n        image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0)\n\n        with torch.no_grad():\n            images = torch.from_numpy(image_data)\n            if self.cuda:\n                images = images.cuda()\n            #---------------------------------------------------------#\n            #   将图像输入网络当中进行预测！\n            #---------------------------------------------------------#\n            outputs = self.net(images)\n            outputs = self.bbox_util.decode_box(outputs)\n            #---------------------------------------------------------#\n            #   将预测框进行堆叠，然后进行非极大抑制\n            #---------------------------------------------------------#\n            results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape, \n                        image_shape, self.letterbox_image, conf_thres=self.confidence, nms_thres=self.nms_iou)\n                                                    \n        t1 = time.time()\n        for _ in range(test_interval):\n            with torch.no_grad():\n                #---------------------------------------------------------#\n                #   将图像输入网络当中进行预测！\n                #---------------------------------------------------------#\n                outputs = self.net(images)\n                outputs = self.bbox_util.decode_box(outputs)\n                #---------------------------------------------------------#\n                #   将预测框进行堆叠，然后进行非极大抑制\n                #---------------------------------------------------------#\n                results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape, \n                            image_shape, self.letterbox_image, conf_thres=self.confidence, nms_thres=self.nms_iou)\n                            \n        t2 = time.time()\n        tact_time = (t2 - t1) / test_interval\n        return tact_time\n\n    def get_map_txt(self, image_id, image, class_names, map_out_path):\n        f = open(os.path.join(map_out_path, \"detection-results/\"+image_id+\".txt\"),\"w\") \n        image_shape = np.array(np.shape(image)[0:2])\n        #---------------------------------------------------------#\n        #   在这里将图像转换成RGB图像，防止灰度图在预测时报错。\n        #   代码仅仅支持RGB图像的预测，所有其它类型的图像都会转化成RGB\n        #---------------------------------------------------------#\n        image       = cvtColor(image)\n        #---------------------------------------------------------#\n        #   给图像增加灰条，实现不失真的resize\n        #   也可以直接resize进行识别\n        #---------------------------------------------------------#\n        image_data  = resize_image(image, (self.input_shape[1],self.input_shape[0]), self.letterbox_image)\n        #---------------------------------------------------------#\n        #   添加上batch_size维度\n        #---------------------------------------------------------#\n        image_data  = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0)\n\n        with torch.no_grad():\n            images = torch.from_numpy(image_data)\n            if self.cuda:\n                images = images.cuda()\n            #---------------------------------------------------------#\n            #   将图像输入网络当中进行预测！\n            #---------------------------------------------------------#\n            outputs = self.net(images)\n            outputs = self.bbox_util.decode_box(outputs)\n            #---------------------------------------------------------#\n            #   将预测框进行堆叠，然后进行非极大抑制\n            #---------------------------------------------------------#\n            results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape, \n                        image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou)\n                                                    \n            if results[0] is None: \n                return \n\n            top_label   = np.array(results[0][:, 6], dtype = 'int32')\n            top_conf    = results[0][:, 4] * results[0][:, 5]\n            top_boxes   = results[0][:, :4]\n\n        for i, c in list(enumerate(top_label)):\n            predicted_class = self.class_names[int(c)]\n            box             = top_boxes[i]\n            score           = str(top_conf[i])\n\n            top, left, bottom, right = box\n            if predicted_class not in class_names:\n                continue\n\n            f.write(\"%s %s %s %s %s %s\\n\" % (predicted_class, score[:6], str(int(left)), str(int(top)), str(int(right)),str(int(bottom))))\n\n        f.close()\n        return ","metadata":{"execution":{"iopub.status.busy":"2022-04-30T06:37:47.024574Z","iopub.execute_input":"2022-04-30T06:37:47.02487Z","iopub.status.idle":"2022-04-30T06:37:47.07972Z","shell.execute_reply.started":"2022-04-30T06:37:47.024839Z","shell.execute_reply":"2022-04-30T06:37:47.078983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# GET_mAP","metadata":{}},{"cell_type":"code","source":"import os\nimport xml.etree.ElementTree as ET\n\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom utils.utils import get_classes\nfrom utils.utils_map import get_coco_map, get_map\n\nif __name__ == \"__main__\":\n    '''\n    Recall和Precision不像AP是一个面积的概念，在门限值不同时，网络的Recall和Precision值是不同的。\n    map计算结果中的Recall和Precision代表的是当预测时，门限置信度为0.5时，所对应的Recall和Precision值。\n\n    此处获得的./map_out/detection-results/里面的txt的框的数量会比直接predict多一些，这是因为这里的门限低，\n    目的是为了计算不同门限条件下的Recall和Precision值，从而实现map的计算。\n    '''\n    #------------------------------------------------------------------------------------------------------------------#\n    #   map_mode用于指定该文件运行时计算的内容\n    #   map_mode为0代表整个map计算流程，包括获得预测结果、获得真实框、计算VOC_map。\n    #   map_mode为1代表仅仅获得预测结果。\n    #   map_mode为2代表仅仅获得真实框。\n    #   map_mode为3代表仅仅计算VOC_map。\n    #   map_mode为4代表利用COCO工具箱计算当前数据集的0.50:0.95map。需要获得预测结果、获得真实框后并安装pycocotools才行\n    #-------------------------------------------------------------------------------------------------------------------#\n    map_mode        = 0\n    #-------------------------------------------------------#\n    #   此处的classes_path用于指定需要测量VOC_map的类别\n    #   一般情况下与训练和预测所用的classes_path一致即可\n    #-------------------------------------------------------#\n    classes_path    = 'model_data/my_classes.txt'\n    #-------------------------------------------------------#\n    #   MINOVERLAP用于指定想要获得的mAP0.x\n    #   比如计算mAP0.75，可以设定MINOVERLAP = 0.75。\n    #-------------------------------------------------------#\n    MINOVERLAP      = 0.5\n    #-------------------------------------------------------#\n    #   map_vis用于指定是否开启VOC_map计算的可视化\n    #-------------------------------------------------------#\n    map_vis         = False\n    #-------------------------------------------------------#\n    #   指向VOC数据集所在的文件夹\n    #   默认指向根目录下的VOC数据集\n    #-------------------------------------------------------#\n    VOCdevkit_path  = 'VOCdevkit'\n    #-------------------------------------------------------#\n    #   结果输出的文件夹，默认为map_out\n    #-------------------------------------------------------#\n    map_out_path    = '/kaggle/working/map_out_1'\n\n    image_ids = open(os.path.join(VOCdevkit_path, \"VOC2007/ImageSets/Main/test.txt\")).read().strip().split()\n\n    if not os.path.exists(map_out_path):\n        os.makedirs(map_out_path)\n    if not os.path.exists(os.path.join(map_out_path, 'ground-truth')):\n        os.makedirs(os.path.join(map_out_path, 'ground-truth'))\n    if not os.path.exists(os.path.join(map_out_path, 'detection-results')):\n        os.makedirs(os.path.join(map_out_path, 'detection-results'))\n    if not os.path.exists(os.path.join(map_out_path, 'images-optional')):\n        os.makedirs(os.path.join(map_out_path, 'images-optional'))\n\n    class_names, _ = get_classes(classes_path)\n\n    if map_mode == 0 or map_mode == 1:\n        print(\"Load model.\")\n        yolo = YOLO(confidence = 0.5, nms_iou = 0.7)\n        print(\"Load model done.\")\n\n        print(\"Get predict result.\")\n        for image_id in tqdm(image_ids):\n            image_path  = os.path.join(VOCdevkit_path, \"VOC2007/JPEGImages/images/\"+image_id+\".jpg\")\n            image       = Image.open(image_path)\n            if map_vis:\n                image.save(os.path.join(map_out_path, \"images-optional/\" + image_id + \".jpg\"))\n            yolo.get_map_txt(image_id, image, class_names, map_out_path)\n        print(\"Get predict result done.\")\n        \n    if map_mode == 0 or map_mode == 2:\n        print(\"Get ground truth result.\")\n        for image_id in tqdm(image_ids):\n            with open(os.path.join(map_out_path, \"ground-truth/\"+image_id+\".txt\"), \"w\") as new_f:\n                root = ET.parse(os.path.join(VOCdevkit_path, \"VOC2007/Annotations/\"+image_id+\".xml\")).getroot()\n                for obj in root.findall('object'):\n                    difficult_flag = False\n                    if obj.find('difficult')!=None:\n                        difficult = obj.find('difficult').text\n                        if int(difficult)==1:\n                            difficult_flag = True\n                    obj_name = obj.find('name').text\n                    if obj_name not in class_names:\n                        continue\n                    bndbox  = obj.find('bndbox')\n                    left    = bndbox.find('xmin').text\n                    top     = bndbox.find('ymin').text\n                    right   = bndbox.find('xmax').text\n                    bottom  = bndbox.find('ymax').text\n\n                    if difficult_flag:\n                        new_f.write(\"%s %s %s %s %s difficult\\n\" % (obj_name, left, top, right, bottom))\n                    else:\n                        new_f.write(\"%s %s %s %s %s\\n\" % (obj_name, left, top, right, bottom))\n        print(\"Get ground truth result done.\")\n\n    if map_mode == 0 or map_mode == 3:\n        print(\"Get map.\")\n        get_map(MINOVERLAP, True, path = map_out_path)\n        print(\"Get map done.\")\n\n    if map_mode == 4:\n        print(\"Get map.\")\n        get_coco_map(class_names = class_names, path = map_out_path)\n        print(\"Get map done.\")","metadata":{"execution":{"iopub.status.busy":"2022-04-30T06:38:02.894475Z","iopub.execute_input":"2022-04-30T06:38:02.894825Z","iopub.status.idle":"2022-04-30T06:40:46.815737Z","shell.execute_reply.started":"2022-04-30T06:38:02.894793Z","shell.execute_reply":"2022-04-30T06:40:46.814786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%cd /kaggle/working/map_out_1/results\n!mv mAP.png /kaggle/working","metadata":{"execution":{"iopub.status.busy":"2022-04-21T15:45:08.63988Z","iopub.execute_input":"2022-04-21T15:45:08.640168Z","iopub.status.idle":"2022-04-21T15:45:09.367021Z","shell.execute_reply.started":"2022-04-21T15:45:08.640135Z","shell.execute_reply":"2022-04-21T15:45:09.366224Z"},"trusted":true},"execution_count":null,"outputs":[]}]}