{"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 numpy as np \nimport pandas as pd\nimport os, sys\nimport zipfile\nimport cv2\nfrom matplotlib import pyplot as plt\nimport shutil \nfrom tqdm import tqdm\nimport torch\nimport torchvision\nimport time\nimport copy\nfrom torchvision import transforms as T, models\nimport torchvision.transforms.functional as F\nfrom path import Path\nimport seaborn as sns\nfrom path import Path\nimport logging\nfrom PIL import Image\nfrom torch.utils.tensorboard import SummaryWriter\nimport math\nimport random\nimport inspect","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:55:59.672398Z","iopub.execute_input":"2022-07-23T19:55:59.673057Z","iopub.status.idle":"2022-07-23T19:56:03.108978Z","shell.execute_reply.started":"2022-07-23T19:55:59.672627Z","shell.execute_reply":"2022-07-23T19:56:03.107867Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* увеличиваем датасет на этапе копирования -- done\n* батч = 4 -- done\n* чб фильтр -- done\n* повороты/аугментации\n    * захватываем тарелку с краями -- done\n* сделать два дополнительных слоя -- done\n* получать оценку качества на основе заданных n трансформаций\n* калибровка\n* отказаться от валидационного множества","metadata":{}},{"cell_type":"markdown","source":"* сначала делаем копии, потом уже разбиваем на тест\\контроль\n* центрировать изображение\n* двигать изображение\n* добавить переспективы в фильтр\n* остальные фильтры стыбзить\n* делать все чисто в чб\n* адам_в вместо адама\n* в конце колабровка\n","metadata":{}},{"cell_type":"markdown","source":"Let's connect to local console to simplify work with command line. In command line we will write following commands to run tensorboard and vizualize plots for accuracy and loss","metadata":{}},{"cell_type":"code","source":"# ! sudo apt install tmate -y\n# tmate (in kaggle)\n# tensorboard dev upload --logdir runs \\\n#     --name \"(optional) My latest experiment\" \\\n#     --description \"(optional) Simple comparison of several hyperparameters\" (in local console, attached to kaggle)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:03.115056Z","iopub.execute_input":"2022-07-23T19:56:03.117544Z","iopub.status.idle":"2022-07-23T19:56:03.123973Z","shell.execute_reply.started":"2022-07-23T19:56:03.117504Z","shell.execute_reply":"2022-07-23T19:56:03.123132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ls /kaggle/input/platesv2","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:03.129067Z","iopub.execute_input":"2022-07-23T19:56:03.132088Z","iopub.status.idle":"2022-07-23T19:56:04.005777Z","shell.execute_reply.started":"2022-07-23T19:56:03.132043Z","shell.execute_reply":"2022-07-23T19:56:04.004204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Logger:\n    def __init__(self, name = 'error_logger'):\n        logger = logging.getLogger(name)\n        logger.setLevel(logging.DEBUG)\n        formatter = logging.Formatter(\"%(asctime)s :: %(message)s\", datefmt=\"%Y-%m-%d %H:%M:%S\")\n\n        ch = logging.StreamHandler()\n        ch.setLevel(logging.DEBUG)\n        ch.setFormatter(formatter)\n\n        fh = logging.FileHandler('log.txt', 'w')\n        fh.setLevel(logging.DEBUG)\n        fh.setFormatter(formatter)\n\n        logger.addHandler(ch)\n        logger.addHandler(fh)\n        self.logger = logger\n        \n    def __call__(self, msg):\n        self.logger.info(msg)\n        \n    def debug(self, msg):\n        self.logger.debug(msg)\n        \n    def info(self, msg):\n        self.logger.info(msg)\n    \n    def error(self, msg):\n        self.logger.error(msg)\n    \n    def warning(self, msg):\n        self.logger.warning(msg)\n    \n    def error(self, msg):\n        self.logger.error(msg)\n    \n    def critical(self, msg):\n        self.logger.critical(msg)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:04.011413Z","iopub.execute_input":"2022-07-23T19:56:04.011822Z","iopub.status.idle":"2022-07-23T19:56:04.045862Z","shell.execute_reply.started":"2022-07-23T19:56:04.011778Z","shell.execute_reply":"2022-07-23T19:56:04.044784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATASET_ZIP = Path('/kaggle/input/platesv2/plates.zip')\n\nwith zipfile.ZipFile(DATASET_ZIP, 'r') as zip_obj:\n    zip_obj.extractall(os.getcwd())","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:04.055481Z","iopub.execute_input":"2022-07-23T19:56:04.060533Z","iopub.status.idle":"2022-07-23T19:56:05.345988Z","shell.execute_reply.started":"2022-07-23T19:56:04.060495Z","shell.execute_reply":"2022-07-23T19:56:05.344953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_ROOT = Path(os.getcwd()) / \"plates\"\nprint(os.listdir(DATA_ROOT))","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:05.347674Z","iopub.execute_input":"2022-07-23T19:56:05.348510Z","iopub.status.idle":"2022-07-23T19:56:05.355528Z","shell.execute_reply.started":"2022-07-23T19:56:05.348471Z","shell.execute_reply":"2022-07-23T19:56:05.354332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def draw_circle(image, circle):\n    cv2.circle(image, (circle[0], circle[1]), circle[2], (255, 0, 0), 2)\n\ndef list_full_paths(dir):\n    return sorted([Path(dir) / x for x in os.listdir(dir) if not x.startswith('.')])\n\ndef draw_rectangle(image, rectangle, color):\n    cv2.rectangle(image, rectangle[:2], rectangle[2:], color, -1) #1","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:05.357365Z","iopub.execute_input":"2022-07-23T19:56:05.358246Z","iopub.status.idle":"2022-07-23T19:56:05.367990Z","shell.execute_reply.started":"2022-07-23T19:56:05.358201Z","shell.execute_reply":"2022-07-23T19:56:05.366959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_mask(image, a=0.3, b=0.8, c=0.95):\n    m, n, _ = image.shape\n    \n    image_rectangle = (0, 0, n, m)\n    \n    unit_scale = min(n, m) / 2\n    \n    sure_foreground_rectangle_coords = np.array([\n        n / 2 - unit_scale * a, \n        m / 2 - unit_scale * a, \n        n / 2 + unit_scale * a, \n        m / 2 + unit_scale * a\n    ]).astype('int')\n\n    pr_backround_rectangle_coords = np.array([\n        n / 2 - unit_scale * b, \n        m / 2 - unit_scale * b, \n        n / 2 + unit_scale * b, \n        m / 2 + unit_scale * b\n    ]).astype('int')\n    \n    sure_backround_rectangle_coords = np.array([\n        0 + unit_scale * (1 - c), \n        0 + unit_scale * (1 - c), \n        n - unit_scale * (1 - c), \n        m - unit_scale * (1 - c)\n    ]).astype('int')\n    \n#     print(\n#         sure_foreground_rectangle_coords,\n#         pr_backround_rectangle_coords,\n#         sure_backround_rectangle_coords\n#     )\n    \n    mask = np.ones(image.shape[:2], np.uint8) * cv2.GC_BGD # 4\n    draw_rectangle(mask, sure_backround_rectangle_coords, cv2.GC_PR_BGD)\n    draw_rectangle(mask, pr_backround_rectangle_coords, cv2.GC_PR_FGD)\n    draw_rectangle(mask, sure_foreground_rectangle_coords, cv2.GC_FGD)\n\n#     plt.imshow(mask, vmin=0, vmax=3, cmap='gray')\n#     plt.show()\n\n    bg_model = np.zeros((1, 65), np.float64)\n    fg_model = np.zeros((1, 65), np.float64)\n\n    cv2.grabCut(image, mask, image_rectangle, bg_model, fg_model, 3, cv2.GC_INIT_WITH_MASK)\n    \n#     plt.imshow(mask, vmin=0, vmax=3, cmap='gray')\n#     plt.show()\n\n    mask[mask == cv2.GC_PR_FGD] = cv2.GC_FGD\n    mask[mask == cv2.GC_PR_BGD] = cv2.GC_BGD\n    mask = cv2.threshold(mask, cv2.GC_FGD - 1, 255, cv2.THRESH_BINARY)[1]\n    \n    return mask\n\ndef get_mask2(image, a=0.9):\n    m, n, _ = image.shape\n    a = 1 - a\n    unit_scale = min(n, m) / 2 \n    rectangle = np.array([\n        0 + unit_scale * a,\n        0 + unit_scale * a, \n        n - unit_scale * a, \n        m - unit_scale * a]).astype('int')\n    \n    mask = np.ones(image.shape[:2], np.uint8) * 0\n    print(mask.shape, rectangle)\n    bg_model = np.zeros((1, 65), np.float64)\n    fg_model = np.zeros((1, 65), np.float64)\n    \n    cv2.grabCut(image, mask, rectangle, bg_model, fg_model, 3, cv2.GC_INIT_WITH_RECT)    \n    mask[mask == cv2.GC_PR_FGD] = cv2.GC_FGD\n    mask[mask == cv2.GC_PR_BGD] = cv2.GC_BGD\n    mask = cv2.threshold(mask, cv2.GC_FGD - 1, 255, cv2.THRESH_BINARY)[1]\n    return mask\n\ndef get_center(mask):\n    moments = cv2.moments(mask)\n    circle_center = (\n        int(moments[\"m10\"] / moments[\"m00\"]\n            ), int(moments[\"m01\"] / moments[\"m00\"])\n    )\n    return circle_center\n\n\ndef get_circle(mask, type='min_radius', verbose=False):\n    circle1 = cv2.HoughCircles(mask, cv2.HOUGH_GRADIENT, 1, 1000, 50, 30, 10, 20).astype('int')[0][0]\n    contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)\n    best_cnt = max(contours, key=cv2.contourArea)\n    circle2 = cv2.minEnclosingCircle(best_cnt)\n    circle2 = [int(x) for x in [*circle2[0], circle2[1]]]\n    if type == '1':\n        return circle1\n    elif type == '2':\n        return circle2\n    elif type == 'min_radius':\n        if hasattr(circle1, '__len__') and circle1[-1] < circle2[-1]:\n            if verbose: print('hough_circles', circle1)\n            return circle1\n        else:\n            if verbose: print('min_enclosure', circle2)\n            return circle2\n    \ndef vizualize_crop(path):\n    image = cv2.imread(path)\n    cropped = crop_plate(image).copy()\n    mask = get_mask(image)\n    circle1 = get_circle(mask, '1')\n    circle2 = get_circle(mask, '2')\n\n    plt.figure(figsize=(20, 4))\n    \n    plt.subplot(1, 4, 1)\n    draw_circle(image, circle1)\n    draw_circle(image, circle2)\n    plt.imshow(image)\n    \n    plt.subplot(1, 4, 2)\n    mask1 = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB)\n    draw_circle(mask1, circle1)\n    plt.imshow(mask1)\n    \n    plt.subplot(1, 4, 3)\n    mask2 = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB)\n    draw_circle(mask2, circle2)\n    plt.imshow(mask2)\n    \n    plt.subplot(1, 4, 4)\n    plt.imshow(cropped)\n    \n    plt.show()\n    \ndef crop_plate(image):\n    circle = get_circle(get_mask(image))\n    r = circle[-1]\n    r *= np.sqrt(2)\n    r = int(r)\n    x1 = max(circle[1] - r, 0)\n    x2 = min(circle[1] + r, image.shape[0])\n    y1 = max(circle[0] - r, 0)\n    y2 = min(circle[0] + r, image.shape[1])\n    new_im = image[x1: x2, y1: y2]\n    return new_im\n    \ndef remove_background(image: np.array, func, *args, **kwargs) -> np.array:\n    mask = func(image, *args, **kwargs)\n    mask //= 255\n    mask = np.moveaxis(np.array([mask, mask, mask]), (0, 1, 2), (2, 0, 1))\n    image = image * mask + (1 - mask) * 255\n    return image\n\n\nclass BackgroundRemover:\n    def __init__(self, func=get_mask, *args, **kwargs):\n        self.args = args\n        self.kwargs = kwargs\n        self.func = func\n    def __call__(self, image: Image) -> Image:\n        image = np.array(image) # PIL -> np.array\n        image = remove_background(image, self.func, *self.args, **self.kwargs)\n        return Image.fromarray(image)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:05.370107Z","iopub.execute_input":"2022-07-23T19:56:05.371071Z","iopub.status.idle":"2022-07-23T19:56:05.419143Z","shell.execute_reply.started":"2022-07-23T19:56:05.371029Z","shell.execute_reply":"2022-07-23T19:56:05.418108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def test():\n    path = Path('plates/train/dirty')\n    rm = BackgroundRemover(get_mask)\n    for file in list_full_paths(path)[:1]:\n        print(file)\n        image = Image.open(file)\n        image = rm(image)\n        print(np.array(image).shape)\n        plt.imshow(image)\n        plt.show()\n    bad_path = 'plates/train/dirty/0014.jpg'\n    print(bad_path)\n    plt.imshow(rm(Image.open(bad_path)))\ntest()","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:05.422162Z","iopub.execute_input":"2022-07-23T19:56:05.422963Z","iopub.status.idle":"2022-07-23T19:56:07.034881Z","shell.execute_reply.started":"2022-07-23T19:56:05.422927Z","shell.execute_reply":"2022-07-23T19:56:07.033871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset_transforms = {\n    '0': T.Compose([]),\n    '90': T.Compose([lambda x: F.rotate(x, 90, expand=True)]),\n    '180': T.Compose([lambda x: F.rotate(x, 180, expand=True)]),\n    '270': T.Compose([lambda x: F.rotate(x, 270, expand=True)])\n}\n\ndef make_augment_copies(path_src, path_dst):\n    if not path_src.endswith('.jpg'): return\n    rm = BackgroundRemover()\n    image = Image.open(path_src)\n    image = rm(image)\n    name = Path(path_src).stem\n    for filt_name, filt in dataset_transforms.items():\n        new_name = name + '_' + filt_name + '.jpg'\n        new_im = filt(image)\n        new_im.save(Path(path_dst) / new_name)\n    os.remove(path_src)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:07.040258Z","iopub.execute_input":"2022-07-23T19:56:07.041292Z","iopub.status.idle":"2022-07-23T19:56:07.052051Z","shell.execute_reply.started":"2022-07-23T19:56:07.041230Z","shell.execute_reply":"2022-07-23T19:56:07.050758Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"CLASS_NAMES = ['cleaned', 'dirty']\n\ndef make_dataset():\n    for class_name in CLASS_NAMES:\n        folder = DATA_ROOT / 'train' / class_name\n        for file in tqdm(list_full_paths(folder)):\n            make_augment_copies(file, folder)\nmake_dataset()","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:07.053476Z","iopub.execute_input":"2022-07-23T19:56:07.054059Z","iopub.status.idle":"2022-07-23T19:56:20.349998Z","shell.execute_reply.started":"2022-07-23T19:56:07.054022Z","shell.execute_reply":"2022-07-23T19:56:20.349066Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TRAIN_DIR = Path('train')\nVAL_DIR = Path('val')\n\ndef split_dataset():\n    for dir_name in [TRAIN_DIR, VAL_DIR]:\n        for class_name in CLASS_NAMES:\n            os.makedirs(dir_name / class_name, exist_ok=True)\n\n    for class_name in CLASS_NAMES:\n        source_dir = DATA_ROOT / 'train' / class_name\n        paths = list_full_paths(source_dir)\n        random.shuffle(paths)\n#         print(paths)\n        for i, path in enumerate(tqdm(paths)):\n            if i % 6 != 0:\n                dest_dir = TRAIN_DIR / class_name\n            else:\n                dest_dir = VAL_DIR / class_name\n            shutil.move(path, dest_dir)\n                \nsplit_dataset()","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:20.353275Z","iopub.execute_input":"2022-07-23T19:56:20.353940Z","iopub.status.idle":"2022-07-23T19:56:20.408191Z","shell.execute_reply.started":"2022-07-23T19:56:20.353888Z","shell.execute_reply":"2022-07-23T19:56:20.407262Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"! ls plates/train/cleaned\n! echo '----'\n! ls train/cleaned","metadata":{"execution":{"iopub.status.busy":"2022-07-23T19:56:20.412267Z","iopub.execute_input":"2022-07-23T19:56:20.416452Z","iopub.status.idle":"2022-07-23T19:56:22.835336Z","shell.execute_reply.started":"2022-07-23T19:56:20.416412Z","shell.execute_reply":"2022-07-23T19:56:22.834215Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class PadCenterCrop(object):\n    def __init__(self, size, pad_if_needed=False, fill=0, padding_mode='constant'):\n        if isinstance(size, (int, float)):\n            self.size = (int(size), int(size))\n        else:\n            self.size = size\n        self.pad_if_needed = pad_if_needed\n        self.padding_mode = padding_mode\n        self.fill = fill\n\n    def __call__(self, img):\n        # pad the width if needed\n        if self.pad_if_needed and img.size[0] < self.size[1]:\n            img = F.pad(img, (self.size[1] - img.size[0], 0), self.fill, self.padding_mode)\n        # pad the height if needed\n        if self.pad_if_needed and img.size[1] < self.size[0]:\n            img = F.pad(img, (0, self.size[0] - img.size[1]), self.fill, self.padding_mode)\n\n        return F.center_crop(img, self.size)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:00:14.975035Z","iopub.execute_input":"2022-07-23T20:00:14.975939Z","iopub.status.idle":"2022-07-23T20:00:14.987242Z","shell.execute_reply.started":"2022-07-23T20:00:14.975877Z","shell.execute_reply":"2022-07-23T20:00:14.986016Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"MEAN = np.array([0.485, 0.456, 0.406])\nSTD = np.array([0.229, 0.224, 0.225])\n\nimage_transforms = T.Compose([\n    T.RandomPerspective(distortion_scale=0.2, p=0.1, interpolation=T.InterpolationMode.BICUBIC, fill=255),\n    T.RandomChoice([\n        T.CenterCrop(180),\n        T.CenterCrop(160),\n        T.CenterCrop(140),\n        T.CenterCrop(120),\n        T.Compose([\n            PadCenterCrop(size=280, pad_if_needed=True, fill=(255, 255, 255)),\n            T.Grayscale(3),\n        ]),\n        T.Compose([\n                T.CenterCrop(200),\n                T.Grayscale(3),\n        ]),\n    ]),\n    T.Resize((224, 224)),\n    T.ColorJitter(hue=(0.1, 0.2)),\n    T.ToTensor(),\n    T.Normalize(MEAN, STD)\n])\n\ntrain_dataset = torchvision.datasets.ImageFolder(TRAIN_DIR, image_transforms)\nval_dataset = torchvision.datasets.ImageFolder(VAL_DIR, image_transforms)\n\nbatch_size = 4\ntrain_dataloader = torch.utils.data.DataLoader(\n    train_dataset, batch_size=batch_size, shuffle=True, num_workers=batch_size)\nval_dataloader = torch.utils.data.DataLoader(\n    val_dataset, batch_size=batch_size, shuffle=False, num_workers=batch_size)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:26:46.800937Z","iopub.execute_input":"2022-07-23T21:26:46.801304Z","iopub.status.idle":"2022-07-23T21:26:46.818198Z","shell.execute_reply.started":"2022-07-23T21:26:46.801262Z","shell.execute_reply":"2022-07-23T21:26:46.817094Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_batch, y_batch = next(iter(train_dataloader))\nplt.imshow(X_batch[0].permute(1, 2, 0).numpy() * STD + MEAN)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:26:49.575504Z","iopub.execute_input":"2022-07-23T21:26:49.575887Z","iopub.status.idle":"2022-07-23T21:26:50.198811Z","shell.execute_reply.started":"2022-07-23T21:26:49.575853Z","shell.execute_reply":"2022-07-23T21:26:50.197835Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_input(input_tensor, title=''):\n    image = input_tensor.permute(1, 2, 0).numpy()\n    image = image * STD + MEAN\n    plt.imshow(image.clip(0, 1))\n    plt.title(title)\n\ndef show_dataloader(dl, labels=None):\n    it = next(iter(dl))\n    X_batch, y_batch = it[0], it[1]\n    if labels is not None:\n        y_batch = labels\n    class_names = ['clean', 'dirty']\n\n    n = 8\n    m = math.ceil(dl.batch_size / n)\n    a = 4\n    i = 1\n    plt.figure(figsize = (n * a, m * a))\n    for x_item, y_item in zip(X_batch, y_batch):\n        plt.subplot(m, n, i)\n        show_input(x_item, title=class_names[y_item] if type(y_item) != str else y_item)\n        i += 1\n\nshow_dataloader(torch.utils.data.DataLoader(train_dataset, batch_size=1000, shuffle=False, num_workers=4))","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:00:15.498558Z","iopub.execute_input":"2022-07-23T20:00:15.499259Z","iopub.status.idle":"2022-07-23T20:00:32.452326Z","shell.execute_reply.started":"2022-07-23T20:00:15.499219Z","shell.execute_reply":"2022-07-23T20:00:32.451109Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"! ls train/cleaned\n! echo -----------\n! ls val/cleaned","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:00:32.454150Z","iopub.execute_input":"2022-07-23T20:00:32.458103Z","iopub.status.idle":"2022-07-23T20:00:34.613598Z","shell.execute_reply.started":"2022-07-23T20:00:32.458055Z","shell.execute_reply":"2022-07-23T20:00:34.612482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"best_model_acc = None\nbest_model_loss = None\nbest_acc = 0\nbest_loss = 1000000\n\ndef train_model(model, loss, optimizer, scheduler, num_epochs):\n    print('{:<7s}|{:^20s}|{:^20s}|'.format('', 'Train', 'Valid'))\n    print('{:<7s}|{:>10s}{:>10s}|{:>10s}{:>10s}|'.format('Epoch', 'Loss', 'Acc', 'Loss', 'Acc'))\n    print('-' * 50)\n    \n    global best_model_acc, best_model_loss, best_acc, best_loss\n    for epoch in range(num_epochs):\n        # Each epoch has a training and validation phase\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                dataloader = train_dataloader\n                scheduler.step()\n                model.train()  # Set model to training mode\n            else:\n                dataloader = val_dataloader\n                model.eval()   # Set model to evaluate mode\n\n            running_loss = 0.\n            running_acc = 0.\n            \n            # Iterate over data.\n            for inputs, labels in dataloader:\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                # forward and backward\n                with torch.set_grad_enabled(phase == 'train'):\n                    preds = model(inputs)\n                    loss_value = loss(preds, labels)\n                    preds_class = preds.argmax(dim=1)\n\n                    # backward + optimize only if in training phase\n                    if phase == 'train':\n                        loss_value.backward()\n                        optimizer.step()\n\n                # statistics\n                running_loss += loss_value.item()\n                running_acc += (preds_class == labels.data).float().mean().data.cpu().numpy()    \n\n            epoch_loss = running_loss / len(dataloader)\n            epoch_acc = running_acc / len(dataloader)\n            \n            global global_epoch\n            \n            writer.add_scalar('Loss/train', epoch_loss, global_epoch)\n            writer.add_scalar('Accuracy/train', epoch_acc, global_epoch)\n            writer.flush()\n            \n            if epoch_loss < best_loss:\n                best_model_loss = copy.deepcopy(model)\n                best_loss = epoch_loss\n            if epoch_acc > best_acc:\n                best_model_acc = copy.deepcopy(model)\n                best_acc = epoch_acc\n\n            if phase == 'train':\n                print('{:>3d}/{:>3d}|{:>10.4f}{:>10.4f}|'.format(epoch + 1, num_epochs, epoch_loss, epoch_acc), end='')\n            else:\n                print('{:>10.4f}{:>10.4f}|'.format(epoch_loss, epoch_acc))\n            loss_history[phase].append(epoch_loss)\n            acc_history[phase].append(epoch_acc)\n            global_epoch += 1\n\n#     return model","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:00:34.617162Z","iopub.execute_input":"2022-07-23T20:00:34.617615Z","iopub.status.idle":"2022-07-23T20:00:34.635274Z","shell.execute_reply.started":"2022-07-23T20:00:34.617582Z","shell.execute_reply":"2022-07-23T20:00:34.634156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass FocalLoss(nn.CrossEntropyLoss):\n    ''' Focal loss for classification tasks on imbalanced datasets '''\n    def __init__(self, gamma, alpha=None, ignore_index=-100, reduction='none'):\n        super().__init__(weight=alpha, ignore_index=ignore_index, reduction='none')\n        self.reduction = reduction\n        self.gamma = gamma\n\n    def forward(self, input_, target):\n        cross_entropy = super().forward(input_, target)\n        # Temporarily mask out ignore index to '0' for valid gather-indices input.\n        # This won't contribute final loss as the cross_entropy contribution\n        # for these would be zero.\n        target = target * (target != self.ignore_index).long()\n        input_prob = torch.gather(F.softmax(input_, 1), 1, target.unsqueeze(1))\n        loss = torch.pow(1 - input_prob, self.gamma) * cross_entropy\n        return torch.mean(loss) if self.reduction == 'mean' \\\n               else torch.sum(loss) if self.reduction == 'sum' \\\n               else loss","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:00:34.637947Z","iopub.execute_input":"2022-07-23T20:00:34.638951Z","iopub.status.idle":"2022-07-23T20:00:34.652219Z","shell.execute_reply.started":"2022-07-23T20:00:34.638915Z","shell.execute_reply":"2022-07-23T20:00:34.651055Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seed = 1\nrandom.seed(seed)\nnp.random.seed(seed)\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed(seed)\ntorch.backends.cudnn.deterministic = True\n\nwriter = SummaryWriter()\nglobal_epoch = 0\nloss_history = {'train':[], 'val':[]}\nacc_history = {'train':[], 'val':[]}\n\nmodel = models.resnet50(pretrained=True)\n\nfor param in model.parameters():\n    param.requires_grad = False\n\nmodel.fc = model.fc = torch.nn.Sequential(\n            torch.nn.Linear(model.fc.in_features, 256),\n            torch.nn.ReLU(),\n            torch.nn.Linear(256, 128),\n            torch.nn.Sigmoid(),\n            torch.nn.Linear(128, 2),\n        )  \ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nprint(device)\nmodel = model.to(device)\n\nloss = torch.nn.CrossEntropyLoss()\n# loss = FocalLoss(1, reduction='mean')\n\noptimizer = torch.optim.Adam(model.parameters(), amsgrad=True, lr=3.0e-4)\nscheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:27:00.276039Z","iopub.execute_input":"2022-07-23T21:27:00.276417Z","iopub.status.idle":"2022-07-23T21:27:00.867332Z","shell.execute_reply.started":"2022-07-23T21:27:00.276384Z","shell.execute_reply":"2022-07-23T21:27:00.866222Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torchvision.transforms.functional as F\n\ntrain_model(model, loss, optimizer, scheduler, num_epochs=30)\nmodel.eval()\nprint()","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:27:10.401716Z","iopub.execute_input":"2022-07-23T21:27:10.402099Z","iopub.status.idle":"2022-07-23T21:28:08.882844Z","shell.execute_reply.started":"2022-07-23T21:27:10.402065Z","shell.execute_reply":"2022-07-23T21:28:08.881624Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs= plt.subplots(1, figsize=(10, 5))\nplt.plot(acc_history['train'], label='train')\nplt.plot(acc_history['val'], label='val')\nplt.legend(loc='lower right')\nplt.grid(linestyle='--', linewidth=0.5, color='.7')\nplt.show()\nprint(best_acc)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:02:00.081333Z","iopub.execute_input":"2022-07-23T20:02:00.081834Z","iopub.status.idle":"2022-07-23T20:02:00.296021Z","shell.execute_reply.started":"2022-07-23T20:02:00.081796Z","shell.execute_reply":"2022-07-23T20:02:00.295060Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TEST_DIR = Path('test')\n\ndef make_test_dataset():\n    rm = BackgroundRemover()\n    dst_path = TEST_DIR / 'unknown'\n    os.makedirs(dst_path, exist_ok=True)\n    for file in tqdm(list_full_paths(DATA_ROOT / 'test')):\n        image = Image.open(file)\n        image = rm(image)\n        image.save(file)\n        shutil.move(file, dst_path)\n        \nmake_test_dataset()","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:04:12.178338Z","iopub.execute_input":"2022-07-23T20:04:12.178694Z","iopub.status.idle":"2022-07-23T20:08:48.218079Z","shell.execute_reply.started":"2022-07-23T20:04:12.178662Z","shell.execute_reply":"2022-07-23T20:08:48.217018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_transforms = {\n    '180': T.Compose([\n        T.CenterCrop(180),\n    ]),\n    '160': T.Compose([\n        T.CenterCrop(160),\n    ]),\n    '140': T.Compose([\n        T.CenterCrop(140),\n    ]),\n    '120': T.Compose([\n        T.CenterCrop(120),\n    ]),\n    '280_g': T.Compose([\n        PadCenterCrop(size=280, pad_if_needed=True, fill=(255, 255, 255)),\n        T.Grayscale(3)\n    ]),\n    '200_g': T.Compose([\n        T.CenterCrop(200),\n        T.Grayscale(3)\n    ])\n}\n\nbasic_transforms = T.Compose([\n    T.Resize((224, 224)),\n    T.ToTensor(),\n    T.Normalize(MEAN, STD)\n])","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:18:31.766062Z","iopub.execute_input":"2022-07-23T20:18:31.766445Z","iopub.status.idle":"2022-07-23T20:18:31.775262Z","shell.execute_reply.started":"2022-07-23T20:18:31.766414Z","shell.execute_reply":"2022-07-23T20:18:31.773942Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ImageFolderWithPaths(torchvision.datasets.ImageFolder):\n    def __getitem__(self, index):\n        original_tuple = super(ImageFolderWithPaths, self).__getitem__(index)\n        path = self.imgs[index][0]\n        tuple_with_path = (original_tuple + (path,))\n        return tuple_with_path\n\n\ntest_dataset = ImageFolderWithPaths('test', None)\n\ntest_dataloader = torch.utils.data.DataLoader(\n    test_dataset, batch_size=1, shuffle=False, num_workers=0)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:18:33.072536Z","iopub.execute_input":"2022-07-23T20:18:33.072880Z","iopub.status.idle":"2022-07-23T20:18:33.085098Z","shell.execute_reply.started":"2022-07-23T20:18:33.072847Z","shell.execute_reply":"2022-07-23T20:18:33.084081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"next(iter(test_dataloader.dataset))[0]","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:18:35.604721Z","iopub.execute_input":"2022-07-23T20:18:35.605095Z","iopub.status.idle":"2022-07-23T20:18:35.637861Z","shell.execute_reply.started":"2022-07-23T20:18:35.605063Z","shell.execute_reply":"2022-07-23T20:18:35.636969Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_wrk = copy.deepcopy(model)\nmodel_wrk.eval()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_predictions():\n    df = {}\n    df['id'] = []\n    for method_name in test_transforms.keys():\n        df[method_name] = []\n    for i, (image, _, path) in tqdm(enumerate(test_dataloader.dataset), total=len(test_dataset)):\n        df['id'].append('%04d' % (i))\n        for method_name, method in test_transforms.items():\n            image2 = method(image)\n            image2 = basic_transforms(image2)\n            image2 = image2.unsqueeze(0)\n            image2 = image2.to(device)\n            with torch.set_grad_enabled(False):\n                preds = model_wrk(image2)\n            label = torch.nn.functional.softmax(preds, dim=1)[:,1].data.cpu().numpy()[0]\n            df[method_name].append(label)\n    df = pd.DataFrame(df)\n    return df\n        \ndf = get_predictions()\n","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:39:30.188778Z","iopub.execute_input":"2022-07-23T20:39:30.189398Z","iopub.status.idle":"2022-07-23T20:40:27.711176Z","shell.execute_reply.started":"2022-07-23T20:39:30.189362Z","shell.execute_reply":"2022-07-23T20:40:27.710149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.mean(axis=1).apply(lambda x: 1 if x > 0.5 else 0).sum() / df.shape[0]","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:50:56.134267Z","iopub.execute_input":"2022-07-23T20:50:56.134848Z","iopub.status.idle":"2022-07-23T20:50:56.147780Z","shell.execute_reply.started":"2022-07-23T20:50:56.134812Z","shell.execute_reply":"2022-07-23T20:50:56.146632Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_distibutions():\n    plt.figure(figsize=(16, 8))\n    for i, column in enumerate(df.columns[1:]):\n        plt.figure(figsize=(16, 8))\n        plt.subplot(2, 3, i + 1)\n        sns.histplot(df[column], bins=10)\n    plt.show()\nplot_distibutions()","metadata":{"execution":{"iopub.status.busy":"2022-07-23T20:51:40.298724Z","iopub.execute_input":"2022-07-23T20:51:40.299090Z","iopub.status.idle":"2022-07-23T20:51:41.352760Z","shell.execute_reply.started":"2022-07-23T20:51:40.299057Z","shell.execute_reply":"2022-07-23T20:51:41.351832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def smooth_plot(lst, diap=0.15):\n    out = []\n    for i in range(100):\n        i /= 100\n        val = lst[(i - diap < lst) & (lst < i + diap)].count()\n        out.append((i, val))\n    out = pd.DataFrame(out)\n    tmp = out[(0.3 <= out[0]) & (out[0] <= 0.7)]\n    m = tmp[tmp[1] == tmp[1].min()]\n    m = m[0].values[0]\n    return m, out[0], out[1]\nplt.plot(*smooth_plot(df['280_g'])[1:])","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:15:03.687379Z","iopub.execute_input":"2022-07-23T21:15:03.688072Z","iopub.status.idle":"2022-07-23T21:15:03.904613Z","shell.execute_reply.started":"2022-07-23T21:15:03.688028Z","shell.execute_reply":"2022-07-23T21:15:03.903695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_smooth_distibutions():\n    plt.figure(figsize=(16, 8))\n    for i, column in enumerate(df.columns[1:]):\n        plt.figure(figsize=(16, 8))\n        plt.subplot(2, 3, i + 1)\n        m, x, y = smooth_plot(df[column])\n        print(m)\n        plt.plot(x, y)\n        plt.show()\nplot_smooth_distibutions()","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:17:39.631197Z","iopub.execute_input":"2022-07-23T21:17:39.631587Z","iopub.status.idle":"2022-07-23T21:17:40.907791Z","shell.execute_reply.started":"2022-07-23T21:17:39.631552Z","shell.execute_reply":"2022-07-23T21:17:40.906836Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def normalize_df(df):\n    df = df.copy()\n    for column in df.columns[1:]:\n        m, _, _ = smooth_plot(df[column])\n        df[column] -= m\n    return df\ndf_n = normalize_df(df)\ndf_n","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:23:51.734839Z","iopub.execute_input":"2022-07-23T21:23:51.735433Z","iopub.status.idle":"2022-07-23T21:23:52.048963Z","shell.execute_reply.started":"2022-07-23T21:23:51.735398Z","shell.execute_reply":"2022-07-23T21:23:52.047854Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_n['label'] = df_n.mean(axis=1).apply(lambda x: 'dirty' if x > 0 else 'cleaned')\ndf_n = df_n[['id', 'label']]\ndf_n","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:30:47.666992Z","iopub.execute_input":"2022-07-23T21:30:47.667616Z","iopub.status.idle":"2022-07-23T21:30:47.692671Z","shell.execute_reply.started":"2022-07-23T21:30:47.667574Z","shell.execute_reply":"2022-07-23T21:30:47.691604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_n.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:30:51.493852Z","iopub.execute_input":"2022-07-23T21:30:51.494327Z","iopub.status.idle":"2022-07-23T21:30:51.504691Z","shell.execute_reply.started":"2022-07-23T21:30:51.494285Z","shell.execute_reply":"2022-07-23T21:30:51.503353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"! rm -rf train val test plates\n! ls","metadata":{"execution":{"iopub.status.busy":"2022-07-23T21:29:09.977506Z","iopub.execute_input":"2022-07-23T21:29:09.978632Z","iopub.status.idle":"2022-07-23T21:29:11.420610Z","shell.execute_reply.started":"2022-07-23T21:29:09.978591Z","shell.execute_reply":"2022-07-23T21:29:11.419377Z"},"trusted":true},"execution_count":null,"outputs":[]}]}