{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.6"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"},{"sourceId":1084583,"sourceType":"datasetVersion","datasetId":604593},{"sourceId":1154184,"sourceType":"datasetVersion","datasetId":652413},{"sourceId":1164586,"sourceType":"datasetVersion","datasetId":659763},{"sourceId":1164617,"sourceType":"datasetVersion","datasetId":659784},{"sourceId":1219292,"sourceType":"datasetVersion","datasetId":648709},{"sourceId":6024453,"sourceType":"datasetVersion","datasetId":648967},{"sourceId":10113011,"sourceType":"datasetVersion","datasetId":6239292}],"dockerImageVersionId":29928,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install --no-deps '../input/timm-package/timm-0.1.26-py3-none-any.whl' > /dev/null\n!pip install --no-deps '../input/pycocotools/pycocotools-2.0-cp37-cp37m-linux_x86_64.whl' > /dev/null","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:09.071346Z","iopub.execute_input":"2024-12-06T14:53:09.071631Z","iopub.status.idle":"2024-12-06T14:53:14.963625Z","shell.execute_reply.started":"2024-12-06T14:53:09.071606Z","shell.execute_reply":"2024-12-06T14:53:14.962695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\n# 添加搜索路径 这里的作用是： python在import的时候，会先从当前目录，已安装和第三方库进行搜索，这里sys.insert可以进行路径的\n# 添加和整理，同时前面的0代表的是优先级别，数字越小，优先级别越高\nsys.path.insert(0, \"../input/timm-efficientdet-pytorch\")\nsys.path.insert(0, \"../input/omegaconf\")\nsys.path.insert(0, \"../input/weightedboxesfusion\")\n\nimport ensemble_boxes \nimport torch\nimport numpy as np\nimport pandas as pd\nfrom glob import glob\nfrom torch.utils.data import Dataset,DataLoader\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\nimport cv2\nimport gc\nfrom matplotlib import pyplot as plt\nfrom effdet import get_efficientdet_config, EfficientDet, DetBenchEval\nfrom effdet.efficientdet import HeadNet\nimport os\nfrom datetime import datetime\nimport time\nimport random\nfrom sklearn.model_selection import StratifiedKFold\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:14.966037Z","iopub.execute_input":"2024-12-06T14:53:14.966553Z","iopub.status.idle":"2024-12-06T14:53:18.194078Z","shell.execute_reply.started":"2024-12-06T14:53:14.966505Z","shell.execute_reply":"2024-12-06T14:53:18.193465Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SEED = 980824\n# 当你固定了这个程序 可以保证你每次跑出来的结果随机数都是相同的，\n# 可以说是固定的部分，你可以加到你想跑的所有程序之中。\ndef seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    # 固定生成随机数的种子 在cuda可以使用的时候 用的是下面cuda的语句\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    # torch.backends.cudnn.benchmark = True 一般加到开头，我们就可以在 PyTorch 中对模型里的卷积层进行预先的优化，\n    # 也就是在每一个卷积层中测试 cuDNN 提供的所有卷积实现算法，然后选择最快的那个。\n    # 这样在模型启动的时候，只要额外多花一点点预处理时间，就可以较大幅度地减少训练时间。\n    # 适用于网络结构不怎么发生变化，同时输入图片尺寸不怎么发生变化的情况。\n    torch.backends.cudnn.benchmark = True\n\nseed_everything(SEED)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:18.195211Z","iopub.execute_input":"2024-12-06T14:53:18.19556Z","iopub.status.idle":"2024-12-06T14:53:18.204207Z","shell.execute_reply.started":"2024-12-06T14:53:18.195524Z","shell.execute_reply":"2024-12-06T14:53:18.203679Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from PIL import Image, ImageDraw\nfrom matplotlib.colors import rgb_to_hsv, hsv_to_rgb\n# rgb_to_hsv 是将图片进行rgb到hsv之间的转变\nimport math","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:18.205561Z","iopub.execute_input":"2024-12-06T14:53:18.205877Z","iopub.status.idle":"2024-12-06T14:53:18.223324Z","shell.execute_reply.started":"2024-12-06T14:53:18.205844Z","shell.execute_reply":"2024-12-06T14:53:18.222784Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_SOURCE = '../input/global-wheat-detection/train.csv'\nWEIGHT = '/kaggle/input/effdet-5-folds/fold2-best-checkpoint-017epoch.bin'\nTRAIN_ROOT_PATH = '../input/global-wheat-detection/train'\nDATA_ROOT_PATH = '../input/global-wheat-detection/train'\n#PL parameters\nEPOCH = 5\nTEST = 11 #1testpl, 11submit\nCONCAT = True\nPL_LR = 0.00005\nPL_BS = 4\nTHRES = 0.17\nSIZE = 512","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:18.226047Z","iopub.execute_input":"2024-12-06T14:53:18.226274Z","iopub.status.idle":"2024-12-06T14:53:18.230554Z","shell.execute_reply.started":"2024-12-06T14:53:18.226253Z","shell.execute_reply":"2024-12-06T14:53:18.229847Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 这里和训练的代码保持一致\nmarking = pd.read_csv(TRAIN_SOURCE)\nbboxs = np.stack(marking['bbox'].apply(lambda x: np.fromstring(x[1:-1], sep=',')))\nfor i, column in enumerate(['x', 'y', 'w', 'h']):\n    marking[column] = bboxs[:,i]# 这里的bbox是是维数组，前面代表行，后面代表列\nmarking.drop(columns=['bbox'], inplace=True)# 这里可以使用drop（column）或者del来删除bbox这一列 \n# inplace=True 说明可以生成新的数据","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:18.232709Z","iopub.execute_input":"2024-12-06T14:53:18.232942Z","iopub.status.idle":"2024-12-06T14:53:19.100222Z","shell.execute_reply.started":"2024-12-06T14:53:18.232919Z","shell.execute_reply":"2024-12-06T14:53:19.099594Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 这里和训练的代码保持一致\ndef get_train_transforms():\n    # alumentations 中我们可以按照自己的需要进行定制化的配置。这里进行的是数据增强\n    return A.Compose(\n        [\n            A.RandomSizedCrop(min_max_height=(800, 800), height=1024, width=1024, p=0.5),\n            # Crop a random part of the input and rescale it to some size.\n            # p (float) – probability of applying the transform. Default: 1.\n            # min_max_height ((int, int)) – crop size limits.\n            # height (int) – height after crop and resize.\n            # width (int) – width after crop and resize.\n            A.OneOf([\n                A.HueSaturationValue(hue_shift_limit=0.2, sat_shift_limit= 0.2, \n                                     val_shift_limit=0.2, p=0.9),\n                # Randomly change hue, saturation and value of the input image.\n                # hue代表的是色调（各种颜色） 饱和度（0白蓝，1蓝）和明度（0暗1亮）\n                # change for changing hue. If hue_shift_limit is a single int, \n                # the range will be (-hue_shift_limit, hue_shift_limit). Default: (-20, 20).\n                A.RandomBrightnessContrast(brightness_limit=0.2, \n                                           contrast_limit=0.2, p=0.9),\n                # Randomly change brightness（亮度） and contrast（对比度） of the input image\n                # 这里的oneof指的是两个里面选择一个\n            ],p=0.9),# 发生oneof的概率是0.9\n            A.ToGray(p=0.01),# 随机将rgb转化为灰度图\n            A.HorizontalFlip(p=0.5),# 竖直反转\n            A.VerticalFlip(p=0.5),# 水平反转\n            A.Resize(height=SIZE, width=SIZE, p=1),#这里将1024的图片变成了512的了\n#             A.Cutout(num_holes=8, max_h_size=64, max_w_size=64, fill_value=0, p=0.5),\n            A.Cutout(num_holes=12, max_h_size=48, max_w_size=48, fill_value=0, p=0.5),\n            # CoarseDropout of the square regions in the image. 随机dropout一些正方形的区域在图片中一些像素\n            # 参数分别代表的是洞的数量，最大高和宽的尺寸填充的值，和概率\n#             A.Cutout(num_holes=8, max_h_size=16, max_w_size=16, fill_value=0, p=0.5),\n#             A.Cutout(num_holes=16, max_h_size=8, max_w_size=8, fill_value=0, p=0.5),\n            ToTensorV2(p=1.0),\n        ], \n        p=1.0, \n        bbox_params=A.BboxParams(\n            format='pascal_voc',\n            min_area=0, \n            min_visibility=0,\n            label_fields=['labels']\n        )# paramaters： {format： of bounding boxes. Should be ‘coco’, ‘pascal_voc’, ‘albumentations’ or ‘yolo’.\n         # min_area：minimum area of a bounding box. All bounding boxes whose visible area in pixels is less than this value will be removed.\n         # minimum fraction of area for a bounding box to remain this box in list.\n         # list of fields that are joined with boxes, e.g labels. Should be same type as boxes.\n    )\n\ndef get_valid_transforms():\n    return A.Compose(\n        [\n            A.Resize(height=SIZE, width=SIZE, p=1.0),\n            ToTensorV2(p=1.0),\n        ], \n        p=1.0, \n        bbox_params=A.BboxParams(\n            format='pascal_voc',\n            min_area=0, \n            min_visibility=0,\n            label_fields=['labels']\n        )\n    )\ndef get_test_transforms():\n    return A.Compose([\n            A.Resize(height=SIZE, width=SIZE, p=1.0),\n            ToTensorV2(p=1.0),\n        ], p=1.0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:19.101632Z","iopub.execute_input":"2024-12-06T14:53:19.101971Z","iopub.status.idle":"2024-12-06T14:53:19.114606Z","shell.execute_reply.started":"2024-12-06T14:53:19.101934Z","shell.execute_reply":"2024-12-06T14:53:19.113953Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\n\nwarnings.filterwarnings(\"ignore\")\n\nclass Fitter:\n    \n    def __init__(self, model, device, config):\n        self.config = config\n        self.epoch = 0\n\n        self.base_dir = f'./{config.folder}'\n        if not os.path.exists(self.base_dir):\n            os.makedirs(self.base_dir)\n        \n        self.log_path = f'{self.base_dir}/log.txt'\n        self.best_summary_loss = 10**5\n\n        self.model = model\n        self.device = device\n\n        param_optimizer = list(self.model.named_parameters())\n        # 这里表示的是需要进行优化的参数\n        no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']\n        optimizer_grouped_parameters = [\n            {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.001},\n            # 这里要进行的是正则化 需要给权重设置weight_decay的数值\n            {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}\n        ] \n\n        self.optimizer = torch.optim.AdamW(self.model.parameters(), lr=config.lr)\n        self.scheduler = config.SchedulerClass(self.optimizer, **config.scheduler_params)\n        self.log(f'Fitter prepared. Device is {self.device}')\n\n    def fit(self, train_loader, validation_loader):\n        for e in range(self.config.n_epochs):\n            if self.config.verbose:\n                lr = self.optimizer.param_groups[0]['lr']\n                timestamp = datetime.utcnow().isoformat()\n                # 这里设置的是世界的时间，用的是isoformat（）\n                self.log(f'\\n{timestamp}\\nLR: {lr}')\n\n            t = time.time()\n            summary_loss = self.train_one_epoch(train_loader)# 下面去找train_one_epoch的函数\n\n            self.log(f'[RESULT]: Train. Epoch: {self.epoch}, summary_loss: {summary_loss.avg:.5f}, time: {(time.time() - t):.5f}')\n            self.save(f'{self.base_dir}/last-checkpoint1.bin')\n\n            t = time.time()\n            summary_loss = self.validation(validation_loader)\n\n            self.log(f'[RESULT]: Val. Epoch: {self.epoch}, summary_loss: {summary_loss.avg:.5f}, time: {(time.time() - t):.5f}')\n            if summary_loss.avg < self.best_summary_loss:\n                self.best_summary_loss = summary_loss.avg\n                self.model.eval()# 这里相当于进入到了inference模式（1）要用于通知dropout层和batchnorm层在train和val模式间切换\n                # 在train模式下，dropout网络层会按照设定的参数p设置保留激活单元的概率（保留概率=p); batchnorm层会继续计算数据的mean和var等参数并更新。\n                # 在val模式下，dropout层会让所有的激活单元都通过，而batchnorm层会停止计算和更新mean和var，直接使用在训练阶段已经学出的mean和var值。\n                # 该模式不会影响各层的gradient计算行为，即gradient计算和存储与training模式一样，只是不进行反传（backprobagation）\n                # 而with torch.zero_grad()则主要是用于停止autograd模块的工作，以起到加速和节省显存的作用，\n                # 具体行为就是停止gradient计算，从而节省了GPU算力和显存，但是并不会影响dropout和batchnorm层的行为。\n                self.save(f'{self.base_dir}/best-checkpoint1.bin')\n                for path in sorted(glob(f'{self.base_dir}/best-checkpoint1.bin'))[:-1]:\n                    os.remove(path)\n\n            if self.config.validation_scheduler:\n                self.scheduler.step(metrics=summary_loss.avg)\n\n            self.epoch += 1\n\n    def validation(self, val_loader):\n        self.model.eval()\n        summary_loss = AverageMeter()\n        t = time.time()\n        for step, (images, targets, image_ids) in enumerate(val_loader):\n            if self.config.verbose:\n                if step % self.config.verbose_step == 0:\n                    print(\n                        f'Val Step {step}/{len(val_loader)}, ' + \\\n                        f'summary_loss: {summary_loss.avg:.5f}, ' + \\\n                        f'time: {(time.time() - t):.5f}', end='\\r'\n                    )\n            with torch.no_grad():\n                images = torch.stack(images)\n                batch_size = images.shape[0]\n                images = images.to(self.device).float()\n                boxes = [target['boxes'].to(self.device).float() for target in targets]\n                labels = [target['labels'].to(self.device).float() for target in targets]\n\n                loss, _, _ = self.model(images, boxes, labels)\n                summary_loss.update(loss.detach().item(), batch_size)\n\n        return summary_loss\n\n    def train_one_epoch(self, train_loader):\n        self.model.train()\n        summary_loss = AverageMeter()\n        t = time.time()\n        for step, (images, targets, image_ids) in enumerate(train_loader):\n            if self.config.verbose:\n                if step % self.config.verbose_step == 0:\n                    print(\n                        f'Train Step {step}/{len(train_loader)}, ' + \\\n                        f'summary_loss: {summary_loss.avg:.5f}, ' + \\\n                        f'time: {(time.time() - t):.5f}', end='\\r'\n                    )\n            \n            images = torch.stack(images)\n            images = images.to(self.device).float()\n            batch_size = images.shape[0]\n            boxes = [target['boxes'].to(self.device).float() for target in targets]\n            labels = [target['labels'].to(self.device).float() for target in targets]\n\n            self.optimizer.zero_grad()\n            \n            loss, _, _ = self.model(images, boxes, labels)\n            \n            loss.backward()\n\n            summary_loss.update(loss.detach().item(), batch_size)\n            # 计算loss的平均值\n\n            self.optimizer.step()\n\n            if self.config.step_scheduler:\n                self.scheduler.step()\n\n        return summary_loss\n    \n    def save(self, path):\n        self.model.eval()\n        torch.save({\n            'model_state_dict': self.model.model.state_dict(),\n            # state_dict是在定义了model或optimizer之后pytorch自动生成的,可以直接调用.\n            # 常用的保存state_dict的格式是\".pt\"或'.pth'的文件,即下面命令的 PATH=\"./***.pt\"\n            'optimizer_state_dict': self.optimizer.state_dict(),\n            'scheduler_state_dict': self.scheduler.state_dict(),\n            'best_summary_loss': self.best_summary_loss,\n            'epoch': self.epoch,\n        }, path)\n\n    def load(self, path):\n        # load_state_dict 也是model或optimizer之后pytorch自动具备的函数,可以直接调用\n        checkpoint = torch.load(path)\n        self.model.model.load_state_dict(checkpoint['model_state_dict'])\n        self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n        self.scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n        self.best_summary_loss = checkpoint['best_summary_loss']\n        self.epoch = checkpoint['epoch'] + 1\n        \n    def log(self, message):\n        if self.config.verbose:\n            print(message)\n        with open(self.log_path, 'a+') as logger:\n            logger.write(f'{message}\\n')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:19.11576Z","iopub.execute_input":"2024-12-06T14:53:19.115973Z","iopub.status.idle":"2024-12-06T14:53:19.142774Z","shell.execute_reply.started":"2024-12-06T14:53:19.115952Z","shell.execute_reply":"2024-12-06T14:53:19.142201Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TrainGlobalConfig:\n    num_workers = 2\n    batch_size = PL_BS\n    n_epochs = EPOCH \n    lr = PL_LR\n\n    folder = 'plabel_model'\n\n    # -------------------\n    verbose = True\n    verbose_step = 1\n    # -------------------\n\n    # --------------------\n    step_scheduler = False  # do scheduler.step after optimizer.step\n    validation_scheduler = True  # do scheduler.step after validation stage loss\n\n    SchedulerClass = torch.optim.lr_scheduler.ReduceLROnPlateau\n    scheduler_params = dict(\n        mode='min',\n        factor=0.5,\n        patience=1,\n        verbose=False, \n        threshold=0.0001,\n        threshold_mode='abs',\n        cooldown=0, \n        min_lr=1e-8,\n        eps=1e-08\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:19.143639Z","iopub.execute_input":"2024-12-06T14:53:19.143844Z","iopub.status.idle":"2024-12-06T14:53:19.156759Z","shell.execute_reply.started":"2024-12-06T14:53:19.143824Z","shell.execute_reply":"2024-12-06T14:53:19.156092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DatasetRetriever(Dataset):\n\n    def __init__(self, image_ids, transforms=None):\n        super().__init__()\n        self.image_ids = image_ids\n        self.transforms = transforms\n\n    def __getitem__(self, index: int):\n        image_id = self.image_ids[index]\n        image = cv2.imread(f'{DATA_ROOT_PATH}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        if self.transforms:\n            sample = {'image': image}\n            sample = self.transforms(**sample)\n            image = sample['image']\n        return image, image_id\n\n    def __len__(self) -> int:\n        return self.image_ids.shape[0]","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:19.157651Z","iopub.execute_input":"2024-12-06T14:53:19.157853Z","iopub.status.idle":"2024-12-06T14:53:19.168469Z","shell.execute_reply.started":"2024-12-06T14:53:19.157834Z","shell.execute_reply":"2024-12-06T14:53:19.16784Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class BaseWheatTTA:\n    \"\"\" author: @shonenkov \"\"\"\n    image_size = 512\n\n    def augment(self, image):\n        raise NotImplementedError\n    \n    def batch_augment(self, images):\n        raise NotImplementedError\n    \n    def deaugment_boxes(self, boxes):\n        raise NotImplementedError\n\nclass TTAHorizontalFlip(BaseWheatTTA):\n    \"\"\" author: @shonenkov \"\"\"\n\n    def augment(self, image):\n        return image.flip(1)\n    \n    def batch_augment(self, images):\n        return images.flip(2)\n    \n    def deaugment_boxes(self, boxes):\n        boxes[:, [1,3]] = self.image_size - boxes[:, [3,1]]\n        return boxes\n\nclass TTAVerticalFlip(BaseWheatTTA):\n    \"\"\" author: @shonenkov \"\"\"\n    \n    def augment(self, image):\n        return image.flip(2)\n    \n    def batch_augment(self, images):\n        return images.flip(3)\n    \n    def deaugment_boxes(self, boxes):\n        boxes[:, [0,2]] = self.image_size - boxes[:, [2,0]]\n        return boxes\n    \nclass TTARotate90(BaseWheatTTA):\n    \"\"\" author: @shonenkov \"\"\"\n    \n    def augment(self, image):\n        return torch.rot90(image, 1, (1, 2))\n\n    def batch_augment(self, images):\n        return torch.rot90(images, 1, (2, 3))\n    \n    def deaugment_boxes(self, boxes):\n        res_boxes = boxes.copy()\n        res_boxes[:, [0,2]] = self.image_size - boxes[:, [1,3]]\n        res_boxes[:, [1,3]] = boxes[:, [2,0]]\n        return res_boxes\n\nclass TTACompose(BaseWheatTTA):\n    \"\"\" author: @shonenkov \"\"\"\n    def __init__(self, transforms):\n        self.transforms = transforms\n        \n    def augment(self, image):\n        for transform in self.transforms:\n            image = transform.augment(image)\n        return image\n    \n    def batch_augment(self, images):\n        for transform in self.transforms:\n            images = transform.batch_augment(images)\n        return images\n    \n    def prepare_boxes(self, boxes):\n        result_boxes = boxes.copy()\n        result_boxes[:,0] = np.min(boxes[:, [0,2]], axis=1)\n        result_boxes[:,2] = np.max(boxes[:, [0,2]], axis=1)\n        result_boxes[:,1] = np.min(boxes[:, [1,3]], axis=1)\n        result_boxes[:,3] = np.max(boxes[:, [1,3]], axis=1)\n        return result_boxes\n    \n    def deaugment_boxes(self, boxes):\n        for transform in self.transforms[::-1]:\n            boxes = transform.deaugment_boxes(boxes)\n        return self.prepare_boxes(boxes)","metadata":{"trusted":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-12-06T14:53:19.169533Z","iopub.execute_input":"2024-12-06T14:53:19.169739Z","iopub.status.idle":"2024-12-06T14:53:19.18742Z","shell.execute_reply.started":"2024-12-06T14:53:19.169719Z","shell.execute_reply":"2024-12-06T14:53:19.186739Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from itertools import product\n\ntta_transforms = []\nfor tta_combination in product([TTAHorizontalFlip(), None], \n                               [TTAVerticalFlip(), None],\n                               [TTARotate90(), None]):\n    tta_transforms.append(TTACompose([tta_transform for tta_transform in tta_combination if tta_transform]))","metadata":{"trusted":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-12-06T14:53:19.188584Z","iopub.execute_input":"2024-12-06T14:53:19.188912Z","iopub.status.idle":"2024-12-06T14:53:19.202857Z","shell.execute_reply.started":"2024-12-06T14:53:19.188878Z","shell.execute_reply":"2024-12-06T14:53:19.202261Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 执行 TTA， 这里试一下\ndef make_tta_predictions(images, score_threshold=0.25):\n    with torch.no_grad():\n        images = torch.stack(images).float().cuda()\n        predictions = []\n        for tta_transform in tta_transforms:\n            result = []\n            det = net(tta_transform.batch_augment(images.clone()), torch.tensor([1]*images.shape[0]).float().cuda())\n\n            for i in range(images.shape[0]):\n                boxes = det[i].detach().cpu().numpy()[:,:4]    \n                scores = det[i].detach().cpu().numpy()[:,4]\n                indexes = np.where(scores > score_threshold)[0]\n                boxes = boxes[indexes]\n                boxes[:, 2] = boxes[:, 2] + boxes[:, 0]\n                boxes[:, 3] = boxes[:, 3] + boxes[:, 1]\n                boxes = tta_transform.deaugment_boxes(boxes.copy())\n                result.append({\n                    'boxes': boxes,\n                    'scores': scores[indexes],\n                })\n            predictions.append(result)\n    return predictions\n\n# 执行模型的融合\ndef run_wbf(predictions, image_index, image_size=512, iou_thr=0.44, skip_box_thr=0.43, weights=None):\n    boxes = [(prediction[image_index]['boxes']/(image_size-1)).tolist() for prediction in predictions]\n    scores = [prediction[image_index]['scores'].tolist() for prediction in predictions]\n    labels = [np.ones(prediction[image_index]['scores'].shape[0]).astype(int).tolist() for prediction in predictions]\n    boxes, scores, labels = ensemble_boxes.ensemble_boxes_wbf.weighted_boxes_fusion(boxes, scores, labels, weights=None, iou_thr=iou_thr, skip_box_thr=skip_box_thr)\n    boxes = boxes*(image_size-1)\n    return boxes, scores, labels","metadata":{"trusted":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-12-06T14:53:19.20375Z","iopub.execute_input":"2024-12-06T14:53:19.203979Z","iopub.status.idle":"2024-12-06T14:53:19.21628Z","shell.execute_reply.started":"2024-12-06T14:53:19.203959Z","shell.execute_reply":"2024-12-06T14:53:19.215515Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 转换预测值的格式\ndef format_prediction_string(boxes, scores):\n    pred_strings = []\n    for j in zip(scores, boxes):\n        pred_strings.append(\"{0:.4f} {1} {2} {3} {4}\".format(j[0], j[1][0], j[1][1], j[1][2], j[1][3]))\n    return \" \".join(pred_strings)","metadata":{"trusted":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2024-12-06T14:53:19.217393Z","iopub.execute_input":"2024-12-06T14:53:19.217691Z","iopub.status.idle":"2024-12-06T14:53:19.231006Z","shell.execute_reply.started":"2024-12-06T14:53:19.217659Z","shell.execute_reply":"2024-12-06T14:53:19.230363Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"marking_test = marking[marking['source'] == 'rres_1']\ntest_image_ids=marking_test['image_id'].drop_duplicates().to_numpy()\nprint(len(test_image_ids))\nprint(test_image_ids[:5])\n\ndataset = DatasetRetriever(\n    image_ids=test_image_ids,\n    transforms=get_test_transforms()\n)\n\ndef collate_fn(batch):\n    return tuple(zip(*batch))\n\ndata_loader = DataLoader(\n    dataset,\n    batch_size=1,\n    shuffle=False,\n    num_workers=4,\n    drop_last=False,\n    collate_fn=collate_fn\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:19.232082Z","iopub.execute_input":"2024-12-06T14:53:19.232374Z","iopub.status.idle":"2024-12-06T14:53:19.255735Z","shell.execute_reply.started":"2024-12-06T14:53:19.23235Z","shell.execute_reply":"2024-12-06T14:53:19.254965Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_net(checkpoint_path):\n    config = get_efficientdet_config('tf_efficientdet_d5')\n    net = EfficientDet(config, pretrained_backbone=False)\n\n    config.num_classes = 1\n    config.image_size=512\n    net.class_net = HeadNet(config, num_outputs=config.num_classes, norm_kwargs=dict(eps=.001, momentum=.01))\n\n    checkpoint = torch.load(checkpoint_path)\n    net.load_state_dict(checkpoint['model_state_dict'])\n    \n#     from _collections import OrderedDict\n#     new_state_dict = OrderedDict()\n#     names = checkpoint['model_state_dict']\n#     for k,v in names.items():\n#         if k[:5] == 'model':\n#             new_key = k[6:]\n#             new_state_dict[new_key] = v\n#     net.load_state_dict(new_state_dict)\n\n    del checkpoint\n    gc.collect()\n\n    net = DetBenchEval(net, config)\n    net.eval();\n    return net.cuda()\n\n# 这里要写你的地址\nnet = load_net(WEIGHT)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:19.256626Z","iopub.execute_input":"2024-12-06T14:53:19.256851Z","iopub.status.idle":"2024-12-06T14:53:28.311566Z","shell.execute_reply.started":"2024-12-06T14:53:19.256828Z","shell.execute_reply":"2024-12-06T14:53:28.310669Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results_plabel = []\n\nfor images, image_ids in data_loader:\n    predictions = make_tta_predictions(images)\n    for i, image in enumerate(images):\n        image_id = image_ids[i]\n        image_ = cv2.imread(f'{DATA_ROOT_PATH}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        h,w,_ = np.shape(image_)\n        boxes, scores, labels = run_wbf(predictions, image_index=i)\n        boxes = (boxes*2).astype(np.int32).clip(min=0, max=1023)\n        image_id = image_ids[i]\n        \n        boxes[:, 2] = boxes[:, 2] - boxes[:, 0]\n        boxes[:, 3] = boxes[:, 3] - boxes[:, 1]\n        \n        boxes = boxes[scores >= THRES].astype(np.int32)\n        scores = scores[scores >=float(THRES)]\n        \n        # make plabel\n        for box in boxes:\n            result_p = {\n                'image_id': image_id,\n                'width':w,\n                'height':h,\n                'source':'rres_1',\n                'x':box[0],\n                'y':box[1],\n                'w':box[2],\n                'h':box[3],\n            }\n            results_plabel.append(result_p)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:53:28.312926Z","iopub.execute_input":"2024-12-06T14:53:28.313221Z","iopub.status.idle":"2024-12-06T14:59:02.395555Z","shell.execute_reply.started":"2024-12-06T14:53:28.313193Z","shell.execute_reply":"2024-12-06T14:59:02.39471Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results_df = pd.DataFrame(results_plabel, columns=['image_id', 'width','height','source','x','y','w','h'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:59:02.396841Z","iopub.execute_input":"2024-12-06T14:59:02.39708Z","iopub.status.idle":"2024-12-06T14:59:02.436908Z","shell.execute_reply.started":"2024-12-06T14:59:02.397055Z","shell.execute_reply":"2024-12-06T14:59:02.436338Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Combine train dataset and pseudo labeling test dataset","metadata":{}},{"cell_type":"code","source":"results_df['image_id'] = results_df['image_id'].apply(lambda x: DATA_ROOT_PATH+'/'+ x+'.jpg')\nmarking['image_id'] = marking['image_id'].apply(lambda x: TRAIN_ROOT_PATH+'/'+ x+'.jpg')\nmarking=marking[marking['source']!='rres_1']\n\nprint(\"Original train dataset\")\nprint(marking.shape)\nprint(\"剩余的 source 种类数量为:\", marking['source'].nunique())\n\nprint(\"Pseudo Labeling\")\nprint(results_df.shape)\nprint(\"剩余的 source 种类数量为:\", results_df['source'].nunique())\n\n# 把测试集的 plabel 和 训练集拼接到一起\nif CONCAT == True:\n    train_data_plabel = pd.concat([results_df,marking], axis=0)\nelse:\n    train_data_plabel = results_df\n\nprint(\"New Training dataset\")\nprint(train_data_plabel.shape)\nprint(\"剩余的 source 种类数量为:\", train_data_plabel['source'].nunique())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T15:11:34.363287Z","iopub.execute_input":"2024-12-06T15:11:34.363619Z","iopub.status.idle":"2024-12-06T15:11:34.489097Z","shell.execute_reply.started":"2024-12-06T15:11:34.363589Z","shell.execute_reply":"2024-12-06T15:11:34.488115Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 分层交叉验证\nskf = StratifiedKFold(n_splits=5, shuffle=True, random_state=980824)\n\ndf_folds = train_data_plabel[['image_id']].copy()\ndf_folds.loc[:, 'bbox_count'] = 1\ndf_folds = df_folds.groupby('image_id').count()\ndf_folds.loc[:, 'source'] = marking[['image_id', 'source']].groupby('image_id').min()['source']\ndf_folds.loc[:, 'stratify_group'] = np.char.add(\n    df_folds['source'].values.astype(str),\n    df_folds['bbox_count'].apply(lambda x: f'_{x // 15}').values.astype(str)\n)\ndf_folds.loc[:, 'fold'] = 0\n\nfor fold_number, (train_index, val_index) in enumerate(skf.split(X=df_folds.index, y=df_folds['stratify_group'])):\n    df_folds.loc[df_folds.iloc[val_index].index, 'fold'] = fold_number\n\n\n# 初始化变量存储 5 个 fold 的总图片数\ntotal_images_all_folds = 0\n\nfor fold_number in range(5):\n    fold_data = df_folds[df_folds['fold'] == fold_number]\n    total_images = len(fold_data)  # 当前 fold 的总图片数\n    source_counts = fold_data['source'].value_counts()  # 按来源统计图片数\n\n    total_images_all_folds += total_images\n    \n    print(f\"Fold {fold_number}:\")\n    print(f\"  Total Images: {total_images}\")\n    print(\"  Source Counts:\")\n    print(source_counts.to_string())  # 打印 source 的统计信息\n    print(\"\\n\")  # 添加换行以便更清晰\n\n# 打印 5 个 fold 的总图片数\nprint(f\"Total Images Across All Folds: {total_images_all_folds}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:59:02.53373Z","iopub.execute_input":"2024-12-06T14:59:02.53408Z","iopub.status.idle":"2024-12-06T14:59:02.891518Z","shell.execute_reply.started":"2024-12-06T14:59:02.534043Z","shell.execute_reply":"2024-12-06T14:59:02.890853Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DatasetRetriever(Dataset):\n\n    def __init__(self, marking, image_ids, transforms=None, test=False):\n        super().__init__()\n\n        self.image_ids = image_ids\n        self.marking = marking\n        self.transforms = transforms\n        self.test = test\n\n    def __getitem__(self, index: int):\n        image_id = self.image_ids[index]\n        \n        if self.test or random.random() > 0.5:\n            image, boxes = self.load_image_and_boxes(index)\n        else:\n            image, boxes = self.load_cutmix_image_and_boxes(index)\n\n        # there is only one class\n        labels = torch.ones((boxes.shape[0],), dtype=torch.int64)\n        \n        target = {}\n        target['boxes'] = boxes\n        target['labels'] = labels\n        target['image_id'] = torch.tensor([index])\n\n        if self.transforms:\n            for i in range(10):\n                sample = self.transforms(**{\n                    'image': image,\n                    'bboxes': target['boxes'],\n                    'labels': labels\n                })\n                if len(sample['bboxes']) > 0:\n                    image = sample['image']\n                    target['boxes'] = torch.stack(tuple(map(torch.tensor, zip(*sample['bboxes'])))).permute(1, 0)\n                    target['boxes'][:,[0,1,2,3]] = target['boxes'][:,[1,0,3,2]]  #yxyx: be warning\n                    break\n\n        return image, target, image_id\n\n    def __len__(self) -> int:\n        return self.image_ids.shape[0]\n\n    def load_image_and_boxes(self, index):\n        image_id = self.image_ids[index]\n        image = cv2.imread(image_id, cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        records = self.marking[self.marking['image_id'] == image_id]\n        boxes = records[['x', 'y', 'w', 'h']].values\n        boxes[:, 2] = boxes[:, 0] + boxes[:, 2]\n        boxes[:, 3] = boxes[:, 1] + boxes[:, 3]\n        return image, boxes\n\n    def load_cutmix_image_and_boxes(self, index, imsize=1024):\n        \"\"\" \n        This implementation of cutmix author:  https://www.kaggle.com/nvnnghia \n        Refactoring and adaptation: https://www.kaggle.com/shonenkov\n        \"\"\"\n        w, h = imsize, imsize\n        s = imsize // 2\n    \n        xc, yc = [int(random.uniform(imsize * 0.25, imsize * 0.75)) for _ in range(2)]  # center x, y\n        # 从一个均匀分布[low,high)中随机采样，注意定义域是左闭右开，即包含low，不包含high.\n        indexes = [index] + [random.randint(0, self.image_ids.shape[0] - 1) for _ in range(3)]\n\n        result_image = np.full((imsize, imsize, 3), 1, dtype=np.float32)\n        result_boxes = []\n\n        for i, index in enumerate(indexes):\n            image, boxes = self.load_image_and_boxes(index)\n            if i == 0:\n                x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc  # xmin, ymin, xmax, ymax (large image)\n                x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h  # xmin, ymin, xmax, ymax (small image)\n            elif i == 1:  # top right\n                x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc\n                x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h\n            elif i == 2:  # bottom left\n                x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)\n                x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, max(xc, w), min(y2a - y1a, h)\n            elif i == 3:  # bottom right\n                x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)\n                x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)\n            result_image[y1a:y2a, x1a:x2a] = image[y1b:y2b, x1b:x2b]\n            padw = x1a - x1b\n            padh = y1a - y1b\n\n            boxes[:, 0] += padw\n            boxes[:, 1] += padh\n            boxes[:, 2] += padw\n            boxes[:, 3] += padh\n\n            result_boxes.append(boxes)\n\n        result_boxes = np.concatenate(result_boxes, 0)\n        np.clip(result_boxes[:, 0:], 0, 2 * s, out=result_boxes[:, 0:])\n        result_boxes = result_boxes.astype(np.int32)\n        result_boxes = result_boxes[np.where((result_boxes[:,2]-result_boxes[:,0])*(result_boxes[:,3]-result_boxes[:,1]) > 0)]\n        return result_image, result_boxes","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:59:02.892626Z","iopub.execute_input":"2024-12-06T14:59:02.892849Z","iopub.status.idle":"2024-12-06T14:59:02.917132Z","shell.execute_reply.started":"2024-12-06T14:59:02.892827Z","shell.execute_reply":"2024-12-06T14:59:02.916264Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fold_number = 2\n\ntrain_dataset = DatasetRetriever(\n    image_ids=df_folds[df_folds['fold'] != fold_number].index.values,\n    marking=train_data_plabel,\n    transforms=get_train_transforms(),\n    test=False,\n)\n\nvalidation_dataset = DatasetRetriever(\n    image_ids=df_folds[df_folds['fold'] == fold_number].index.values,\n    marking=train_data_plabel,\n    transforms=get_valid_transforms(),\n    test=True,\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:59:02.918141Z","iopub.execute_input":"2024-12-06T14:59:02.918412Z","iopub.status.idle":"2024-12-06T14:59:02.933755Z","shell.execute_reply.started":"2024-12-06T14:59:02.918389Z","shell.execute_reply":"2024-12-06T14:59:02.933213Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))\n\ndef run_training():\n    device = torch.device('cuda:0')\n    net.to(device)\n\n    train_loader = torch.utils.data.DataLoader(\n        train_dataset,\n        batch_size=TrainGlobalConfig.batch_size,\n        sampler=RandomSampler(train_dataset),\n        pin_memory=False,\n        drop_last=True,\n        num_workers=TrainGlobalConfig.num_workers,\n        collate_fn=collate_fn,\n    )\n    val_loader = torch.utils.data.DataLoader(\n        validation_dataset, \n        batch_size=TrainGlobalConfig.batch_size,\n        num_workers=TrainGlobalConfig.num_workers,\n        shuffle=False,\n        sampler=SequentialSampler(validation_dataset),\n        pin_memory=False,\n        collate_fn=collate_fn,\n    )\n\n    fitter = Fitter(model=net, device=device, config=TrainGlobalConfig)\n    fitter.fit(train_loader, val_loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:59:02.935013Z","iopub.execute_input":"2024-12-06T14:59:02.935333Z","iopub.status.idle":"2024-12-06T14:59:02.946171Z","shell.execute_reply.started":"2024-12-06T14:59:02.9353Z","shell.execute_reply":"2024-12-06T14:59:02.945283Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from effdet import get_efficientdet_config, EfficientDet, DetBenchTrain\nfrom effdet.efficientdet import HeadNet\n\ndef get_net():\n#     config = get_efficientdet_config('tf_efficientdet_d5')\n#     net = EfficientDet(config, pretrained_backbone=False)\n\n#     config.num_classes = 1\n#     config.image_size=512\n#     net.class_net = HeadNet(config, num_outputs=config.num_classes, norm_kwargs=dict(eps=.001, momentum=.01))\n\n#     checkpoint = torch.load(checkpoint_path)\n#     net.load_state_dict(checkpoint['model_state_dict'])\n\n#     del checkpoint\n#     gc.collect()\n    \n    config = get_efficientdet_config('tf_efficientdet_d5')\n    net = EfficientDet(config, pretrained_backbone=False)\n    \n    config.num_classes = 1\n    config.image_size = SIZE\n    net.class_net = HeadNet(config, num_outputs=config.num_classes, norm_kwargs=dict(eps=.001, momentum=.01))\n    checkpoint = torch.load(WEIGHT)\n    net.load_state_dict(checkpoint['model_state_dict'])\n   \n    return DetBenchTrain(net, config)\n\nnet = get_net()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:59:02.947196Z","iopub.execute_input":"2024-12-06T14:59:02.947462Z","iopub.status.idle":"2024-12-06T14:59:04.418139Z","shell.execute_reply.started":"2024-12-06T14:59:02.947414Z","shell.execute_reply":"2024-12-06T14:59:04.417424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class AverageMeter(object):\n    \"\"\"Computes and stores the average and current value\"\"\"\n    def __init__(self):\n        self.reset()\n\n    def reset(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n\n    def update(self, val, n=1):\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:59:04.420311Z","iopub.execute_input":"2024-12-06T14:59:04.42068Z","iopub.status.idle":"2024-12-06T14:59:04.426282Z","shell.execute_reply.started":"2024-12-06T14:59:04.420643Z","shell.execute_reply":"2024-12-06T14:59:04.42564Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if len(os.listdir(DATA_ROOT_PATH))<TEST:\n    pass\nelse:\n    run_training()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-06T14:59:04.427173Z","iopub.execute_input":"2024-12-06T14:59:04.427454Z","iopub.status.idle":"2024-12-06T15:07:25.308867Z","shell.execute_reply.started":"2024-12-06T14:59:04.42741Z","shell.execute_reply":"2024-12-06T15:07:25.308129Z"}},"outputs":[],"execution_count":null}]}