{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames[:3]:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-01-02T05:16:32.215731Z","iopub.execute_input":"2022-01-02T05:16:32.21646Z","iopub.status.idle":"2022-01-02T05:16:37.977882Z","shell.execute_reply.started":"2022-01-02T05:16:32.216418Z","shell.execute_reply":"2022-01-02T05:16:37.977002Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/tensorflow-great-barrier-reef/train.csv')\ntrain","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:37.979997Z","iopub.execute_input":"2022-01-02T05:16:37.980508Z","iopub.status.idle":"2022-01-02T05:16:38.058008Z","shell.execute_reply.started":"2022-01-02T05:16:37.980463Z","shell.execute_reply":"2022-01-02T05:16:38.056959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['path'] = 'video_' + train['video_id'].astype(str) + '/' + train['video_frame'].astype(str) + '.jpg'\ntrain","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:38.059187Z","iopub.execute_input":"2022-01-02T05:16:38.05944Z","iopub.status.idle":"2022-01-02T05:16:38.155637Z","shell.execute_reply.started":"2022-01-02T05:16:38.05941Z","shell.execute_reply":"2022-01-02T05:16:38.154763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.head(30)","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:38.158038Z","iopub.execute_input":"2022-01-02T05:16:38.158545Z","iopub.status.idle":"2022-01-02T05:16:38.174971Z","shell.execute_reply.started":"2022-01-02T05:16:38.15849Z","shell.execute_reply":"2022-01-02T05:16:38.174255Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['annotations'][16] #'[]' 문자형","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:38.176148Z","iopub.execute_input":"2022-01-02T05:16:38.176523Z","iopub.status.idle":"2022-01-02T05:16:38.189863Z","shell.execute_reply.started":"2022-01-02T05:16:38.176492Z","shell.execute_reply":"2022-01-02T05:16:38.189159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['annotations'] = train['annotations'].apply(eval) #리스트로 변환\ntrain['annotations'][0]","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:38.191161Z","iopub.execute_input":"2022-01-02T05:16:38.191594Z","iopub.status.idle":"2022-01-02T05:16:38.545763Z","shell.execute_reply.started":"2022-01-02T05:16:38.191561Z","shell.execute_reply":"2022-01-02T05:16:38.544743Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['annotations'][0] #list형식 []","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:38.546993Z","iopub.execute_input":"2022-01-02T05:16:38.547233Z","iopub.status.idle":"2022-01-02T05:16:38.553106Z","shell.execute_reply.started":"2022-01-02T05:16:38.547203Z","shell.execute_reply":"2022-01-02T05:16:38.552322Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train[train['annotations'].str.len()>0].reset_index(drop = True)\ntrain.head(60)","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:26:12.63671Z","iopub.execute_input":"2022-01-02T05:26:12.637584Z","iopub.status.idle":"2022-01-02T05:26:12.71682Z","shell.execute_reply.started":"2022-01-02T05:26:12.637531Z","shell.execute_reply":"2022-01-02T05:26:12.715661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pd.DataFrame(train['annotations'][0]).astype(float).values #이렇게 해놓으면 됨","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:38.625524Z","iopub.execute_input":"2022-01-02T05:16:38.625846Z","iopub.status.idle":"2022-01-02T05:16:38.634027Z","shell.execute_reply.started":"2022-01-02T05:16:38.62581Z","shell.execute_reply":"2022-01-02T05:16:38.633366Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\ntorch.ones(5)","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:38.636543Z","iopub.execute_input":"2022-01-02T05:16:38.637201Z","iopub.status.idle":"2022-01-02T05:16:39.925904Z","shell.execute_reply.started":"2022-01-02T05:16:38.637163Z","shell.execute_reply":"2022-01-02T05:16:39.92501Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torch.utils.data import DataLoader, Dataset\nimport torch\nimport cv2\n\nclass ReefDatatset(Dataset):\n    def __init__(self, df, transforms = None):\n        super().__init__() #데이터셋을 상속받는다\n        self.df = df\n        self.transforms = transforms\n    def __len__(self):   #몇개의 데이터를 처리해야하는지\n        return len(self.df) #모든 이미지에 대해 처리 \n    def __getitem__(self, i): #데이터를 하나씩 가져온다\n        row = self.df.iloc[i]\n        #이미지, 바운딩, 정답 클래스에 대한 처리\n        image = cv2.imread('/kaggle/input/tensorflow-great-barrier-reef/train_images/'+row['path'], 1) #1 :컬러로 가져오겠다\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32) #파일 비트 맞춰줘야함\n        image = image/255.0 #값이 튀는걸 막기 위해 0-1사이로\n        \n        boxes = pd.DataFrame(row['annotations']).astype(float).values\n        boxes[:,2] = boxes[:,0] + boxes[:,2]   #width->x max\n        boxes[:,3] = boxes[:,1] + boxes[:,3]   #height->y max\n        \n        boxes[:,2] = np.clip(boxes[:,2], 0, 1280) #0.4+0.6=1.00001을 막기 위해(범위 넘으면 오류) 부동소수점 문제\n        boxes[:,3] = np.clip(boxes[:,3], 0, 720)\n        \n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0]) #바운딩박스의 넓이가 점수에 영향을 줌-> 그래서 넣어준 것(꼭 필요하지는 x)\n        area = torch.as_tensor(area, dtype=torch.float32) #tensor형식으로 들어가있어야 가중치 쓸 수 있음\n        \n        #정답값 만들기 (맞다 아니다)\n        labels = torch.ones(len(boxes), dtype = torch.int64) #근데 ones로 한 이유? tensor형식으로 들어가야 하니까 -> 다중분류에서는 torch.tensor(이미지 정답 클래스)\n        \n        iscrowd = torch.zeros((len(boxes),), dtype=torch.int64) #모든 클래스가 0일 때(아닐 때).. 거기에 뭔가 있긴 하다 (없어도 되긴 하나 특정 물체를 있냐 없냐 판단하는 loss값 계산할때 필요(점수개선))\n        \n        target = {}\n        target['boxes'] = boxes\n        target['labels'] = labels\n        # target['masks'] = None\n        target['image_id'] = torch.tensor([i])\n        target['area'] = area\n        target['iscrowd'] = iscrowd\n\n        if self.transforms: #if문 있어도되고 없어도 됨\n            sample = { #딕셔너리를 만드는 이유 1. augmentation 2.image랑 bounding box는 tensor형식으로 안바꿨음->바꿔야함\n                'image': image,\n                'bboxes': target['boxes'], #bboxes !!!\n                'labels': labels\n            }\n            sample = self.transforms(**sample) # **는 index의 이름을 제대로 적어줘야 한다. key값에 맞게 잘 들어와야 한다-> dic형식으로 들어온다. #transforms에서 augmentation적용이 된다 아래 train_transform\n            image = sample['image'] #aug적용이 되어따\n            \n            if len(boxes) > 0:\n                target['boxes'] = torch.stack(tuple(map(torch.tensor, zip(*sample['bboxes'])))).permute(1, 0) #bounding을 tensor로 바꾸면 차원이 바뀌어버림 -> permute로 다시 돌림\n#             else:\n#                 target['boxes'] =  torch.as_tensor(target['boxes'], dtype=torch.float32) #이거 필요 없음?\n        \n        return image, target #target 엔 바운딩박스+레이블 정보 들어감","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:39.927815Z","iopub.execute_input":"2022-01-02T05:16:39.928328Z","iopub.status.idle":"2022-01-02T05:16:40.145221Z","shell.execute_reply.started":"2022-01-02T05:16:39.928263Z","shell.execute_reply":"2022-01-02T05:16:40.144545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\ndef train_transform():\n    return A.Compose([ToTensorV2()], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']}) #augmentation #파스칼 voc형식(x min max...  형식)","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:40.146369Z","iopub.execute_input":"2022-01-02T05:16:40.147135Z","iopub.status.idle":"2022-01-02T05:16:42.717262Z","shell.execute_reply.started":"2022-01-02T05:16:40.147097Z","shell.execute_reply":"2022-01-02T05:16:42.716091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = ReefDatatset(train, train_transform())","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:42.718738Z","iopub.execute_input":"2022-01-02T05:16:42.71947Z","iopub.status.idle":"2022-01-02T05:16:42.72406Z","shell.execute_reply.started":"2022-01-02T05:16:42.71942Z","shell.execute_reply":"2022-01-02T05:16:42.723326Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch)) # * => 모델이 바운딩박스가 몇개 들어갈 지 모를 때 * !! (옵션이 몇개 들어갈지 모를때)","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:42.725678Z","iopub.execute_input":"2022-01-02T05:16:42.726158Z","iopub.status.idle":"2022-01-02T05:16:42.73981Z","shell.execute_reply.started":"2022-01-02T05:16:42.72611Z","shell.execute_reply":"2022-01-02T05:16:42.738882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#data loader\ntrain_dataloader = DataLoader(train_dataset, batch_size = 8, num_workers = 4, collate_fn = collate_fn) # 하나의 데이터에 대해서 여러개의 바운딩박스 #ReefDatatset에 리턴값이 두개니까","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:42.740931Z","iopub.execute_input":"2022-01-02T05:16:42.741564Z","iopub.status.idle":"2022-01-02T05:16:42.753014Z","shell.execute_reply.started":"2022-01-02T05:16:42.741508Z","shell.execute_reply":"2022-01-02T05:16:42.752064Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"collate_fn\n한 이미지에 대해 여러개의 바운딩박스로 묶일 수 있게","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nimage, targets = train_dataset[0]\n\n\nboxes = targets['boxes'].cpu().numpy().astype(np.int32)\nimg = image.permute(1,2,0).cpu().numpy()\nfig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\nfor box in boxes:\n    cv2.rectangle(img,\n                  (box[0], box[1]),\n                  (box[2], box[3]),\n                  (220, 0, 0), 3)\n    \nax.set_axis_off()\nax.imshow(img);","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:42.754168Z","iopub.execute_input":"2022-01-02T05:16:42.754486Z","iopub.status.idle":"2022-01-02T05:16:43.547873Z","shell.execute_reply.started":"2022-01-02T05:16:42.75445Z","shell.execute_reply":"2022-01-02T05:16:43.546361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image, targets = train_dataset[50]\n\n\nboxes = targets['boxes'].cpu().numpy().astype(np.int32)\nimg = image.permute(1,2,0).cpu().numpy()\nfig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\nfor box in boxes:\n    cv2.rectangle(img,\n                  (box[0], box[1]),\n                  (box[2], box[3]),\n                  (220, 0, 0), 3)\n    \nax.set_axis_off()\nax.imshow(img);","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:43.549213Z","iopub.execute_input":"2022-01-02T05:16:43.549681Z","iopub.status.idle":"2022-01-02T05:16:44.257246Z","shell.execute_reply.started":"2022-01-02T05:16:43.549633Z","shell.execute_reply":"2022-01-02T05:16:44.256339Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image, targets = train_dataset[370]\n\n\nboxes = targets['boxes'].cpu().numpy().astype(np.int32) #.cpu().numpy()? tensor로 바꿨었는데 그럼 그림이 안그려짐 그래서 array로 바꿔야함\nimg = image.permute(1,2,0).cpu().numpy()\nfig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\nfor box in boxes:\n    cv2.rectangle(img,\n                  (box[0], box[1]),\n                  (box[2], box[3]),\n                  (220, 0, 80), 3)\n    \nax.set_axis_off()\nax.imshow(img);","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:44.258856Z","iopub.execute_input":"2022-01-02T05:16:44.259161Z","iopub.status.idle":"2022-01-02T05:16:44.937214Z","shell.execute_reply.started":"2022-01-02T05:16:44.259123Z","shell.execute_reply":"2022-01-02T05:16:44.93608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset[50]","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:44.938582Z","iopub.execute_input":"2022-01-02T05:16:44.93936Z","iopub.status.idle":"2022-01-02T05:16:45.00186Z","shell.execute_reply.started":"2022-01-02T05:16:44.939288Z","shell.execute_reply":"2022-01-02T05:16:45.001153Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#모델링\n\nimport torchvision #모델\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor #출력층\n\nmodel = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained = True) #분류모델은 resnet으로 많이 함 #pretrained = True 미리 학습된 가중치를 가져온다. *pretrained = False?\nmodel","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:45.003257Z","iopub.execute_input":"2022-01-02T05:16:45.004151Z","iopub.status.idle":"2022-01-02T05:16:51.786426Z","shell.execute_reply.started":"2022-01-02T05:16:45.004106Z","shell.execute_reply":"2022-01-02T05:16:51.785488Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"(roi_heads): RoIHeads => 출력층\n\n(box_predictor): FastRCNNPredictor => 얘를 바꿔\n(box_predictor): FastRCNNPredictor(\n      (cls_score): Linear(in_features=1024, out_features=91, bias=True) #클래스 관련 처리 (91개 인데 우리는 2개만 필요)\n      \n      \n      (bbox_pred): Linear(in_features=1024, out_features=364, bias=True) #바운딩 박스 관련 처리","metadata":{}},{"cell_type":"code","source":"model.roi_heads.box_predictor = FastRCNNPredictor(1024, 2) #층에 들어오는 노드의 개수 #클래스 2개\nmodel","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:51.787633Z","iopub.execute_input":"2022-01-02T05:16:51.787871Z","iopub.status.idle":"2022-01-02T05:16:51.798133Z","shell.execute_reply.started":"2022-01-02T05:16:51.78784Z","shell.execute_reply":"2022-01-02T05:16:51.797161Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device('cuda')\nmodel.to(device)","metadata":{"execution":{"iopub.status.busy":"2022-01-02T06:40:23.248232Z","iopub.execute_input":"2022-01-02T06:40:23.248587Z","iopub.status.idle":"2022-01-02T06:40:23.337348Z","shell.execute_reply.started":"2022-01-02T06:40:23.248483Z","shell.execute_reply":"2022-01-02T06:40:23.336154Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"params = [p for p in model.parameters() if p.requires_grad] #층 고정 -> 시간절역 -> 점수가 조금 안나올 수 있음 (속도가 중요할 때 필요!)\nparams","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:51.957475Z","iopub.status.idle":"2022-01-02T05:16:51.958184Z","shell.execute_reply.started":"2022-01-02T05:16:51.957887Z","shell.execute_reply":"2022-01-02T05:16:51.957921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"optimizer = torch.optim.SGD(params, lr = 0.01, momentum=0, weight_decay=0,) #lr을 0.005, 0.0025줄이자*** #momentum은 옛날의 방향을 믿을것인가","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:51.959755Z","iopub.status.idle":"2022-01-02T05:16:51.960438Z","shell.execute_reply.started":"2022-01-02T05:16:51.960134Z","shell.execute_reply":"2022-01-02T05:16:51.960166Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#loss 값 계산 (항상 이렇게 씀)\nclass Averager:\n    def __init__(self):\n        self.current_total = 0.0 #여기에 loss값 저장\n        self.iterations = 0.0 #한번씩 늘어나는 값 초기화\n\n    def send(self, value):\n        self.current_total += value\n        self.iterations += 1\n\n    @property\n    def value(self):\n        if self.iterations == 0:\n            return 0\n        else:\n            return 1.0 * self.current_total / self.iterations\n\n    def reset(self):\n        self.current_total = 0.0\n        self.iterations = 0.0","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:51.962006Z","iopub.status.idle":"2022-01-02T05:16:51.962716Z","shell.execute_reply.started":"2022-01-02T05:16:51.962437Z","shell.execute_reply":"2022-01-02T05:16:51.962468Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loss_hist = Averager()","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:51.964419Z","iopub.status.idle":"2022-01-02T05:16:51.965111Z","shell.execute_reply.started":"2022-01-02T05:16:51.96482Z","shell.execute_reply":"2022-01-02T05:16:51.96485Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"itr = 1\n\nfor epoch in range(2):\n    loss_hist.reset() \n    \n    for images, targets in train_dataloader: #배치 개념마다 #출력값 두개 (ReefDatatset에서 리턴값이 두개였음)\n        \n        images = list(image.float().to(device) for image in images) #디텍션이라  list로\n        targets = [{k: v.to(torch.float32).to(device) if \"box\" in k else v.to(device) for k, v in t.items()} for t in targets] \n\n        loss_dict = model(images, targets)\n        \n        losses = sum(loss for loss in loss_dict.values()) #4가지 loss중 누구를 기준으로 업데이트할건지? (기본은 4개 모두 고려 sum)\n        \n        loss_value = losses.item() #losses는 텐서 형식으로 감싸져있어서 숫자만 가져오기 위해\n\n        loss_hist.send(loss_value)\n\n        optimizer.zero_grad() #초기화\n        losses.backward() #역전파 실제 학습 (losses가 텐서 형식으로 되어있고 sum값이기 때문에 losses.로 된다 -> tensor형식이어야 미분이 가능해짐 !!)\n        optimizer.step() #가중치 업데이트\n\n        if itr % 50 == 0:\n            print(f\"Iteration #{itr} loss: {loss_value}\") #케라스에서는 에폭따라 개선이 보이는데 파이토치는 독립적인 loss를 가져오기 떄문에 왔다갔다 , 좋아지지 않음\n    \n        itr += 1\n    \n#     # update the learning rate\n#     if lr_scheduler is not None:\n#         lr_scheduler.step()\n\n    print(f\"Epoch #{epoch} loss: {loss_hist.value}\")   ","metadata":{"execution":{"iopub.status.busy":"2022-01-02T05:16:51.966592Z","iopub.status.idle":"2022-01-02T05:16:51.967258Z","shell.execute_reply.started":"2022-01-02T05:16:51.966966Z","shell.execute_reply":"2022-01-02T05:16:51.966994Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}