{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":84969,"databundleVersionId":10033515,"sourceType":"competition"},{"sourceId":9867543,"sourceType":"datasetVersion","datasetId":6040935},{"sourceId":211097053,"sourceType":"kernelVersion"},{"sourceId":244281,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":208687,"modelId":230374}],"dockerImageVersionId":30805,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":234.328586,"end_time":"2024-12-04T21:48:03.797664","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2024-12-04T21:44:09.469078","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Pre-Train + Fine-tuning (Day 9) \n\n+ PredictionAggregator 방법 시도 (Submission 시간 확인 및 성능)\n\n+ 규화님 Parameter","metadata":{}},{"cell_type":"markdown","source":"# Install and Import modules","metadata":{}},{"cell_type":"code","source":"!tar xfvz /kaggle/input/ultralytics-for-offline-install/archive.tar.gz\n!pip install --no-index --find-links=./packages ultralytics\n!rm -rf ./packages","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:56:43.999223Z","iopub.execute_input":"2025-01-28T15:56:43.999571Z","iopub.status.idle":"2025-01-28T15:57:52.102991Z","shell.execute_reply.started":"2025-01-28T15:56:43.999528Z","shell.execute_reply":"2025-01-28T15:57:52.101812Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!cp -r '/kaggle/input/hengck-czii-cryo-et-01/wheel_file' '/kaggle/working/'\n!pip install /kaggle/working/wheel_file/asciitree-0.3.3/asciitree-0.3.3\n!pip install --no-index --find-links=/kaggle/working/wheel_file zarr","metadata":{"papermill":{"duration":52.635478,"end_time":"2024-12-04T21:46:09.434099","exception":false,"start_time":"2024-12-04T21:45:16.798621","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:57:52.105927Z","iopub.execute_input":"2025-01-28T15:57:52.106314Z","iopub.status.idle":"2025-01-28T15:58:44.962110Z","shell.execute_reply.started":"2025-01-28T15:57:52.106275Z","shell.execute_reply":"2025-01-28T15:58:44.961223Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zarr\nfrom ultralytics import YOLO\nfrom tqdm import tqdm\nimport glob, os\nimport torch\nimport time\n\n\nimport sys\nimport warnings\nimport math\nfrom scipy.spatial import cKDTree\nfrom collections import defaultdict","metadata":{"papermill":{"duration":6.112534,"end_time":"2024-12-04T21:46:15.553505","exception":false,"start_time":"2024-12-04T21:46:09.440971","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:58:44.963451Z","iopub.execute_input":"2025-01-28T15:58:44.963822Z","iopub.status.idle":"2025-01-28T15:58:53.716186Z","shell.execute_reply.started":"2025-01-28T15:58:44.963781Z","shell.execute_reply":"2025-01-28T15:58:53.715531Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport cv2","metadata":{"papermill":{"duration":0.660793,"end_time":"2024-12-04T21:46:16.240893","exception":false,"start_time":"2024-12-04T21:46:15.5801","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:58:53.717128Z","iopub.execute_input":"2025-01-28T15:58:53.717664Z","iopub.status.idle":"2025-01-28T15:58:54.099791Z","shell.execute_reply.started":"2025-01-28T15:58:53.717636Z","shell.execute_reply":"2025-01-28T15:58:54.098900Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nsys.setrecursionlimit(10000)","metadata":{"papermill":{"duration":0.013236,"end_time":"2024-12-04T21:46:16.261298","exception":false,"start_time":"2024-12-04T21:46:16.248062","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:58:54.100890Z","iopub.execute_input":"2025-01-28T15:58:54.101346Z","iopub.status.idle":"2025-01-28T15:58:54.105243Z","shell.execute_reply.started":"2025-01-28T15:58:54.101319Z","shell.execute_reply":"2025-01-28T15:58:54.104461Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.simplefilter('ignore')\nnp.warnings = warnings","metadata":{"papermill":{"duration":0.012104,"end_time":"2024-12-04T21:46:16.299355","exception":false,"start_time":"2024-12-04T21:46:16.287251","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:58:54.106498Z","iopub.execute_input":"2025-01-28T15:58:54.106864Z","iopub.status.idle":"2025-01-28T15:58:54.119026Z","shell.execute_reply.started":"2025-01-28T15:58:54.106835Z","shell.execute_reply":"2025-01-28T15:58:54.118421Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Prepare trained YOLO model","metadata":{}},{"cell_type":"code","source":"model = YOLO(\"/kaggle/input/day9/pytorch/default/1/best.pt\")","metadata":{"papermill":{"duration":2.090864,"end_time":"2024-12-04T21:46:18.396896","exception":false,"start_time":"2024-12-04T21:46:16.306032","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:58:56.820777Z","iopub.execute_input":"2025-01-28T15:58:56.821352Z","iopub.status.idle":"2025-01-28T15:58:57.589018Z","shell.execute_reply.started":"2025-01-28T15:58:56.821319Z","shell.execute_reply":"2025-01-28T15:58:57.588069Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"runs_path = sorted(glob.glob('/kaggle/input/czii-cryo-et-object-identification/test/static/ExperimentRuns/*'))\nruns = sorted(runs_path)  # 기존 코드에서 잘못된 glob.glob 호출 제거\nruns = [os.path.basename(run) for run in runs]\n\n# 데이터 분할\nsp = len(runs) // 2\nruns1 = runs[:sp]\nruns2 = runs[sp:]\n\n# 확인\nprint(\"Runs1:\", runs1[:5])\nprint(\"Runs2:\", runs2[:5])\n\n# GPU 개수 검증\nassert torch.cuda.device_count() == 2","metadata":{"papermill":{"duration":0.02108,"end_time":"2024-12-04T21:46:18.425141","exception":false,"start_time":"2024-12-04T21:46:18.404061","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:59:01.325134Z","iopub.execute_input":"2025-01-28T15:59:01.325508Z","iopub.status.idle":"2025-01-28T15:59:01.370430Z","shell.execute_reply.started":"2025-01-28T15:59:01.325477Z","shell.execute_reply":"2025-01-28T15:59:01.369637Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"particle_names = [\n    'apo-ferritin',\n    'beta-amylase',\n    'beta-galactosidase',\n    'ribosome',\n    'thyroglobulin',\n    'virus-like-particle'\n]\n\nparticle_to_index = {\n    'apo-ferritin': 0,\n    'beta-amylase': 1,\n    'beta-galactosidase': 2,\n    'ribosome': 3,\n    'thyroglobulin': 4,\n    'virus-like-particle': 5\n}\n\nindex_to_particle = {index: name for name, index in particle_to_index.items()}\n\nparticle_radius = {\n    'apo-ferritin': 60,\n    'beta-amylase': 65,\n    'beta-galactosidase': 90,\n    'ribosome': 150,\n    'thyroglobulin': 130,\n    'virus-like-particle': 135,\n}","metadata":{"papermill":{"duration":0.01279,"end_time":"2024-12-04T21:46:18.497067","exception":false,"start_time":"2024-12-04T21:46:18.484277","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:59:04.654475Z","iopub.execute_input":"2025-01-28T15:59:04.654822Z","iopub.status.idle":"2025-01-28T15:59:04.660216Z","shell.execute_reply.started":"2025-01-28T15:59:04.654795Z","shell.execute_reply":"2025-01-28T15:59:04.659342Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# define Main process class\nThere are many variables and functions going back and forth. We can easily manage variables by defining classes.","metadata":{}},{"cell_type":"code","source":"# add by @sesasj\nclass UnionFind:\n    def __init__(self, size):\n        self.parent = np.arange(size)\n        self.rank = np.zeros(size, dtype=int)\n\n    def find(self, u):\n        if self.parent[u] != u:\n            self.parent[u] = self.find(self.parent[u])  \n        return self.parent[u]\n\n    def union(self, u, v):\n        u_root = self.find(u)\n        v_root = self.find(v)\n        if u_root == v_root:\n            return\n            \n        if self.rank[u_root] < self.rank[v_root]:\n            self.parent[u_root] = v_root\n        else:\n            self.parent[v_root] = u_root\n            if self.rank[u_root] == self.rank[v_root]:\n                self.rank[u_root] += 1\n\nclass PredictionAggregator:\n    def __init__(self, first_conf=0.2, conf_coef=0.75):\n        self.first_conf = first_conf\n        self.conf_coef = conf_coef\n        self.particle_confs = np.array([0.5, 0.0, 0.2, 0.5, 0.2, 0.5])\n        \n    def convert_to_8bit(self, volume):\n        lower, upper = np.percentile(volume, (0.5, 99.5))\n        clipped = np.clip(volume, lower, upper)\n        scaled = ((clipped - lower) / (upper - lower + 1e-12) * 255).astype(np.uint8)\n        return scaled\n\n    def make_predictions(self, run_id, model, device_no):\n        volume_path = f'/kaggle/input/czii-cryo-et-object-identification/test/static/ExperimentRuns/{run_id}/VoxelSpacing10.000/denoised.zarr'\n        volume = zarr.open(volume_path, mode='r')[0]\n        volume_8bit = self.convert_to_8bit(volume)\n        num_slices = volume_8bit.shape[0]\n\n        detections = {\n            'particle_type': [],\n            'confidence': [],\n            'x': [],\n            'y': [],\n            'z': []\n        }\n\n        for slice_idx in range(num_slices):\n            \n            img = volume_8bit[slice_idx]\n            input_image = cv2.resize(np.stack([img]*3, axis=-1), (640, 640))\n\n            results = model.predict(\n                input_image,\n                save=False,\n                imgsz=640,\n                conf=self.first_conf,\n                device=device_no,\n                batch=1,\n                verbose=False,\n            )\n\n            for result in results:\n                boxes = result.boxes\n                if boxes is None:\n                    continue\n                cls = boxes.cls.cpu().numpy().astype(int)\n                conf = boxes.conf.cpu().numpy()\n                xyxy = boxes.xyxy.cpu().numpy()\n\n                xc = ((xyxy[:, 0] + xyxy[:, 2]) / 2.0) * 10 * (63/64) # 63/64 because of the resize\n                yc = ((xyxy[:, 1] + xyxy[:, 3]) / 2.0) * 10 * (63/64)\n                zc = np.full(xc.shape, slice_idx * 10 + 5)\n\n                particle_types = [index_to_particle[c] for c in cls]\n\n                detections['particle_type'].extend(particle_types)\n                detections['confidence'].extend(conf)\n                detections['x'].extend(xc)\n                detections['y'].extend(yc)\n                detections['z'].extend(zc)\n\n        if not detections['particle_type']:\n            return pd.DataFrame()  \n\n        particle_types = np.array(detections['particle_type'])\n        confidences = np.array(detections['confidence'])\n        xs = np.array(detections['x'])\n        ys = np.array(detections['y'])\n        zs = np.array(detections['z'])\n\n        aggregated_data = []\n\n        for idx, particle in enumerate(particle_names):\n            if particle == 'beta-amylase':\n                continue \n\n            mask = (particle_types == particle)\n            if not np.any(mask):\n                continue  \n                \n            particle_confidences = confidences[mask]\n            particle_xs = xs[mask]\n            particle_ys = ys[mask]\n            particle_zs = zs[mask]\n            # -------------modified by @sersasj ------------------------\n            coords = np.vstack((particle_xs, particle_ys, particle_zs)).T\n\n           \n            z_distance = 30 # How many slices can you \"jump\" to aggregate predictions 10 = 1, 20 = 2...\n            xy_distance = 20 # xy_tol_p2 in original code by ITK8191\n            \n            max_distance = math.sqrt(z_distance**2 + xy_distance**2)\n            tree = cKDTree(coords)            \n            pairs = tree.query_pairs(r=max_distance, p=2)\n\n            \n            uf = UnionFind(len(coords))\n            \n            coords_xy = coords[:, :2]\n            coords_z = coords[:, 2]\n            for u, v in pairs:\n                z_diff = abs(coords_z[u] - coords_z[v])\n                if z_diff > z_distance:\n                    continue  \n\n                xy_diff = np.linalg.norm(coords_xy[u] - coords_xy[v])\n                if xy_diff > xy_distance:\n                    continue  \n\n                uf.union(u, v)\n\n            roots = np.array([uf.find(i) for i in range(len(coords))])\n            unique_roots, inverse_indices, counts = np.unique(roots, return_inverse=True, return_counts=True)\n            conf_sums = np.bincount(inverse_indices, weights=particle_confidences)\n            \n            aggregated_confidences = conf_sums / (counts ** self.conf_coef)\n            cluster_per_particle = [4,1,2,9,4,8]\n            valid_clusters = (counts >= cluster_per_particle[idx]) & (aggregated_confidences > self.particle_confs[idx])\n\n            if not np.any(valid_clusters):\n                continue  \n\n            cluster_ids = unique_roots[valid_clusters]\n\n            centers_x = np.bincount(inverse_indices, weights=particle_xs) / counts\n            centers_y = np.bincount(inverse_indices, weights=particle_ys) / counts\n            centers_z = np.bincount(inverse_indices, weights=particle_zs) / counts\n\n            centers_x = centers_x[valid_clusters]\n            centers_y = centers_y[valid_clusters]\n            centers_z = centers_z[valid_clusters]\n\n            aggregated_df = pd.DataFrame({\n                'experiment': [run_id] * len(centers_x),\n                'particle_type': [particle] * len(centers_x),\n                'x': centers_x,\n                'y': centers_y,\n                'z': centers_z\n            })\n\n            aggregated_data.append(aggregated_df)\n\n        if aggregated_data:\n            return pd.concat(aggregated_data, axis=0)\n        else:\n            return pd.DataFrame() ","metadata":{"papermill":{"duration":0.028228,"end_time":"2024-12-04T21:46:18.531807","exception":false,"start_time":"2024-12-04T21:46:18.503579","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:59:07.154995Z","iopub.execute_input":"2025-01-28T15:59:07.155791Z","iopub.status.idle":"2025-01-28T15:59:07.180180Z","shell.execute_reply.started":"2025-01-28T15:59:07.155755Z","shell.execute_reply":"2025-01-28T15:59:07.179299Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# instance main class\naggregator = PredictionAggregator(first_conf=0.19,  conf_coef=0.34) #Update\naggregated_results = []\n#add by @minfuka\nfrom concurrent.futures import ProcessPoolExecutor #add by @minfuka\n\n#add by @minfuka\ndef inference(runs, model, device_no):\n    subs = []\n    for r in tqdm(runs, total=len(runs)):\n        df = aggregator.make_predictions(r, model, device_no)\n        subs.append(df)\n    \n    return subs\nstart_time = time.time()\n\nwith ProcessPoolExecutor(max_workers=2) as executor:\n    results = list(executor.map(inference, (runs1, runs2), (model, model), (\"0\", \"1\")))\n\n\nend_time = time.time()\n\nestimated_total_time = (end_time - start_time) / len(runs) * 500  \nprint(f'estimated total prediction time for 500 runs: {estimated_total_time:.4f} seconds')","metadata":{"papermill":{"duration":0.012195,"end_time":"2024-12-04T21:46:18.550571","exception":false,"start_time":"2024-12-04T21:46:18.538376","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:59:07.715922Z","iopub.execute_input":"2025-01-28T15:59:07.716648Z","iopub.status.idle":"2025-01-28T15:59:36.519297Z","shell.execute_reply.started":"2025-01-28T15:59:07.716612Z","shell.execute_reply":"2025-01-28T15:59:36.518182Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#change by @minfuka\nsubmission0 = pd.concat(results[0])\nsubmission1 = pd.concat(results[1])\nsubmission = pd.concat([submission0, submission1]).reset_index(drop=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:59:37.326939Z","iopub.execute_input":"2025-01-28T15:59:37.327730Z","iopub.status.idle":"2025-01-28T15:59:37.334610Z","shell.execute_reply.started":"2025-01-28T15:59:37.327694Z","shell.execute_reply":"2025-01-28T15:59:37.333733Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.insert(0, 'id', range(len(submission)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:59:40.393853Z","iopub.execute_input":"2025-01-28T15:59:40.394197Z","iopub.status.idle":"2025-01-28T15:59:40.406279Z","shell.execute_reply.started":"2025-01-28T15:59:40.394167Z","shell.execute_reply":"2025-01-28T15:59:40.405445Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T15:59:41.123933Z","iopub.execute_input":"2025-01-28T15:59:41.124272Z","iopub.status.idle":"2025-01-28T15:59:41.149977Z","shell.execute_reply.started":"2025-01-28T15:59:41.124244Z","shell.execute_reply":"2025-01-28T15:59:41.149078Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index=False)\nsubmission.head()","metadata":{"papermill":{"duration":0.020972,"end_time":"2024-12-04T21:48:02.239453","exception":false,"start_time":"2024-12-04T21:48:02.218481","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-01-28T16:01:00.092164Z","iopub.execute_input":"2025-01-28T16:01:00.092865Z","iopub.status.idle":"2025-01-28T16:01:00.113847Z","shell.execute_reply.started":"2025-01-28T16:01:00.092831Z","shell.execute_reply":"2025-01-28T16:01:00.112932Z"}},"outputs":[],"execution_count":null}]}