|
|
1) faster r-cnn 모델의 구조와 원리를 조사하라
https://arxiv.org/pdf/1506.01497
https://junha1125.github.io/blog/artificial-intelligence/2020-08-15-1FastRCNN/
faster r-cnn은 기존 r-cnn의 탐색에 들이는 리소스를 줄이기 위해 지역 제안 네트워크가 추가되고 분류와 별개로 적용되어 효율적으로 개발된 모델이다.
faster r-cnn은 기존 CNN을 통해 특징을 추출하는 Feature Extraction Network를 거쳐 특징 맵을 추출한다.
이후 Region Proposal Network(RPN)을 통해 물체가 있을만한 가능성을 가진 후보영역을 추출한다.
RPN은 이후 기존 특성 맵에서 해당 영역을 추출하여 Region of Interest Pooling(Rol 풀링)과정을 수행하여 고정된 크기로 변환한다.
마지막으로 완결 연결층(FC)를 통해 각 후보 영역에 대한 물체의 클래스와 경계 상자 좌표를 예측한다.
'''
2) 다음 2가지 방식으로 훈련결과를 실시간으로 시각화하시오.
- 원본은 건드리지 말고 새로운 소스파일 my_train.py 생성하여 원본을 복사하여 작업하고 시각화 코드를 적당한 위치에 추가할것
- 훈련로스, 정확도, 검증로스, 검증정확도 그래프 출력기능을 다음 2가지 방법으로 구현
- 교재 626페이지 코드를 이용하는 방법, 텐서보드를 사용하여 시각화하는 방법
- 매 에퍽마다 실시간으로 그래프를 업데이트하도록 할것 -> 훈련하는 동안 시간에 따라 로스의 변화를 확인가능하도록 할것 -> 손실이 더이상 감소하지 않거나 감소하다가 다시 증가한다면 훈련중단해야함
'''
3) 최종 모델파일을 이용하여 테스트하는 파이토치 코드 my_test.py를 작성하시오.
테스트영상은 로봇사진을 각각 10장씩 새로 찍어서 사용하라
파일을 개별적으로 오픈하지 말고 파이썬 os 패키지는 이용하여 폴더명을 주면 폴더에 저장된 영상파일을 모두 검색하여 불러와서 테스트하도록 할것
영상 1장의 추론(예측)시간을 측정하여 출력하시오.
참고 : https://learnopencv.com/faster-r-cnn-object-detection-with-pytorch/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | import os import time from PIL import Image import matplotlib.pyplot as plt import torch import torchvision.transforms as T import torchvision import numpy as np import cv2 # 모델을 로드 def load_model(model_path): model = torchvision.models.detection.fasterrcnn_resnet50_fpn(weights=torchvision.models.detection.FasterRCNN_ResNet50_FPN_Weights.COCO_V1) model.load_state_dict(torch.load(model_path)) model.eval() return model def get_prediction(filename, model, threshold, class_list): start_time = time.time() COCO_INSTANCE_CATEGORY_NAMES = class_list img = Image.open(filename) transform = T.Compose([T.ToTensor()]) img = transform(img) pred = model([img]) end_time = time.time() pred_class = [COCO_INSTANCE_CATEGORY_NAMES[i] for i in list(pred[0]['labels'].numpy())] pred_boxes = [[(i[0], i[1]), (i[2], i[3])] for i in list(pred[0]['boxes'].detach().numpy())] pred_score = list(pred[0]['scores'].detach().numpy()) pred_t = [pred_score.index(x) for x in pred_score if x > threshold][-1] pred_boxes = pred_boxes[:pred_t + 1] pred_class = pred_class[:pred_t + 1] print("Check_predict_time:", end_time - start_time) return pred_boxes, pred_class def object_detection_api(img_path, model, class_list, threshold=0.5, rect_th=3, text_size=3, text_th=3): for filename in os.listdir(img_path): if filename.lower().endswith('.jpg'): file = os.path.join(img_path, filename) boxes, pred_cls = get_prediction(file, model, threshold, class_list) img = cv2.imread(file) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) for i in range(len(boxes)): cv2.rectangle(img, boxes[i][0], boxes[i][1], color=(0, 255, 0), thickness=rect_th) cv2.putText(img, pred_cls[i], boxes[i][0], cv2.FONT_HERSHEY_SIMPLEX, text_size, (0, 255, 0), thickness=text_th) plt.figure(figsize=(20, 30)) plt.imshow(img) plt.xticks([]) plt.yticks([]) plt.show() def main(test_path, model_path): # 모델 로드 model = load_model(model_path) COCO_INSTANCE_CATEGORY_NAMES = [ '__background__', 'big robot', 'small robot' ] threshold = 0.5 object_detection_api(img_path=test_path, model=model, class_list=COCO_INSTANCE_CATEGORY_NAMES, threshold=threshold) if __name__ == "__main__": test_path = 'D:/pytorch_cuda/detection_data/dataset1/test' model_path = 'D:/pytorch_cuda/detection_data/dataset1/output/model.pth' main(test_path, model_path) | cs |
학습한 모델과 테스트 코드의 모델을 동일하게 설정해보았으나 같은 에러 동일하게 나타남.
모델 호출하는 load_model에서 문제가 있는것으로 생각하고 불러오는 코드를 수정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #모델을 로드 def load_model(model_path): #모델 정의 model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=False) num_classes = 3 in_features = model.roi_heads.box_predictor.in_features model.roi_heads.box_predictor = FastRCNNPredictor(in_channels=in_features, num_classes=num_classes) #전체 모델 구조 포함 로드 model = torch.load(model_path) #가중치만 가져올 때 #model.load_state_dict(state_dict) model.eval() return model | cs |
마지막 진행 상황 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | import os import time from PIL import Image import matplotlib.pyplot as plt import torch import torchvision import torchvision.transforms as T from torchvision.models.detection.faster_rcnn import FastRCNNPredictor import numpy as np import cv2 #모델을 로드 def load_model(model_path): #모델 정의 model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=False) num_classes = 3 in_features = model.roi_heads.box_predictor.in_features model.roi_heads.box_predictor = FastRCNNPredictor(in_channels=in_features, num_classes=num_classes) #전체 모델 구조 포함 로드 #model = torch.load(model_path) #가중치만 가져올 때 model.load_state_dict(torch.load(model_path, map_location=torch.device('cuda'))) model.eval() return model def get_prediction(filename, model, threshold, class_list): start_time = time.time() COCO_INSTANCE_CATEGORY_NAMES = class_list img = Image.open(filename) transform = T.Compose([T.ToTensor()]) img = transform(img) pred = model([img]) end_time = time.time() pred_class = [COCO_INSTANCE_CATEGORY_NAMES[i] for i in list(pred[0]['labels'].numpy())] pred_boxes = [[(i[0], i[1]), (i[2], i[3])] for i in list(pred[0]['boxes'].detach().numpy())] pred_score = list(pred[0]['scores'].detach().numpy()) pred_t = [pred_score.index(x) for x in pred_score if x > threshold][-1] pred_boxes = pred_boxes[:pred_t + 1] pred_class = pred_class[:pred_t + 1] print("Check_predict_time:", end_time - start_time) return pred_boxes, pred_class def object_detection_api(img_path, model, class_list, threshold=0.5, rect_th=3, text_size=3, text_th=3): for filename in os.listdir(img_path): if filename.lower().endswith('.jpg'): file = os.path.join(img_path, filename) boxes, pred_cls = get_prediction(file, model, threshold, class_list) img = cv2.imread(file) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) for i in range(len(boxes)): cv2.rectangle(img, boxes[i][0], boxes[i][1], color=(0, 255, 0), thickness=rect_th) cv2.putText(img, pred_cls[i], boxes[i][0], cv2.FONT_HERSHEY_SIMPLEX, text_size, (0, 255, 0), thickness=text_th) plt.figure(figsize=(20, 30)) plt.imshow(img) plt.xticks([]) plt.yticks([]) plt.show() def main(test_path, model_path): # 모델 로드 model = load_model(model_path) COCO_INSTANCE_CATEGORY_NAMES = [ '__background__', 'big robot', 'small robot' ] threshold = 0.5 object_detection_api(img_path=test_path, model=model, class_list=COCO_INSTANCE_CATEGORY_NAMES, threshold=threshold) if __name__ == "__main__": test_path = 'D:/pytorch_cuda/detection_data/dataset1/test' model_path = 'D:/pytorch_cuda/detection_data/dataset1/output/model.pth' main(test_path, model_path) | cs |
후에 추가 참고 링크
4) 최종 모델파일을 이용하여 테스트하는 opencv C++ 코드 my_test.cpp를 작성하시오.
테스트영상은 로봇사진을 각각 10장씩 새로 찍어서 사용하라
파일을 개별적으로 오픈하지 말고 C++ filesystem 라이브러리를 이용하여 폴더명을 주면 폴더에 저장된 영상파일을 모두 검색하여 불러와 서 테스트하도록 할것
영상 1장의 추론(예측)시간을 측정하여 출력하시오.
참고 : https://modoocode.com/306
5) 훈련 성능을 평가하는 아래의 지표들의 의미를 설명하시오.
| loss | 전체 손실값으로 모델의 예측과 실제 클래스 객체 사이의 차이를 이야기한다. |
| loss_classifier | Image Classification에 대한 손실값으로 객체가 특정 클래스에 대한 차이를 이야기한다. 값이 낮을수록 전체적인 분류가 잘 되었다고 판별한다. |
| loss_box_reg | 객체의 위치 예측에 대한 손실값으로 예측된 바운딩 박스와 실제 바운딩 박스 간의 loss값을 알려준다. |
| loss_objectness | 객체의 존재에 대한 여부를 가르킨다. 특정 영역에 객체가 존재하는지 아닌지에 대한 loss값을 알려준다. |
| loss_rpn_box_reg | RPN, 박스 회귀 손실로 객체가 있을 가능성이 높은 영역을 제한하는 것으로 제안된 바운딩 박스와 실제 위치가 일치하는지에 대한 loss값을 알려준다. |
| Average Precision(AP) | 평균 정밀도로 IoU와 maxDets 등등에 대한 조건에 따른 정밀도와 재현율을 종합하여 계산된다. |
| Intersection over Union(IoU) | 예측된 바운딩 박스와 실제 바운딩 박스 간의 겹치는 부분의 비율을 측정한다. IoU값이 높을수록 객체가 예측값과 일치한다. |
| maxDets | 모델이 감지하는 최대 객체 수를 의미한다. 위 지표들이 높은 순으로 나열하여 maxDets만큼의 데이터를 처리한다. |
6) 아래 표의 의미를 설명하라.
COCO-dataset 구조 설명
https://cocodataset.org/#detection-eval
Average Precision(AP) : 평균 정밀도로 모델이 양성으로 예측한 값중 실제로 양성인 비율이다. 해당 값이 높다면 오검출이 낮다는 의미이다. IOU, area, maxDets의 결과에 따른 정밀도가 된다.
Average Recall(AR) : 평균 재현율로 실제 양성중에서 모델이 정확하게 감지한 비율이다. 해당 값이 높다면 미검출한 객체가 낮다는 뜻이다. IOU, area, maxDets의 결과에 따른 재현율이 된다.
IoU : 사람이 설정한 객체의 rounding box와 AI가 추측한 객체의 rounding box의 일치하는 비율을 나타낸 값으로 IoU=0.50은 50% 이상 겹치는 객체에 대해서 정답으로 판별한다는 의미이고 IoU=0.5:0.95는 50%이상 95%미만 겹치는 영역에 위치했을 때 정답으로 판별한다는 의미이다.
area : rounding box의 크기를 의미하는 것으로 컴퓨터가 추측하는 rounding box를 이야기한다.
coco-dataset에서 설정되어 있는 기준으로 단위는 pixel이다.
해당 변수에 대해서 실제 객체의 크기에 비해 설정한 area의 크기가 작다면 acc가 -1.000으로 나오는 경우가 있다.
위 표에서도 small과 medium에 대해서만 -1이 출력되고 나머지 area에 대해서는 나오지 않는 것을 확인할 수 있다.
maxDets : CNN에서 추출한 클래스 객체가 있을 것이라고 추정되는 area에 대해 몇개의 area에 대해 추정하는 가에 대한 지표로 확률이 높은 순으로 정렬하여 해당 개수만큼 검사를 진행한다. maxDets가 1인 경우에는 area 하나에 대해서만 클래스 분류를 진행한다.
7) 전이학습에서 훈련하는 웨이트는 컨벌루션레이어와 완전연결레이어중 어느것인가?
컨벌루션 레이어는 은닉층에서 사용되는 레이어들이고 완전 연결 레이어는 은닉층의 가중치와 편향값의 수행 결과를 토대로 모델의 최종 출력을 결정하는 역할을 하는 레이어로 전이학습에서 재학습하는 레이어들은 컨벌루션 레이어이다.
8) 백본모델을 resnet101 모델로 변경하여 훈련을 실행하고 resnet50과 성능을 비교하시오.
ResNet101
1 2 3 4 5 6 7 8 | #import from torchvision.models import ResNet101_Weights #set mask #parser.add_argument("--model", default="maskrcnn_resnet50_fpn", type=str, help="model name") parser.add_argument("--model", default="maskrcnn_resnet101_fpn", type=str, help="model name") #backbone_weights system #parser.add_argument("--weights-backbone", default=None, type=str, help="the backbone weights enum name to load") parser.add_argument("--weights-backbone", default=ResNet101_Weights.IMAGENET1K_V1, type=str, help="the backbone weights enum name to load") | cs |
마스크 ResNet50_FPN와 backbone의 레이어 층을 일치시켜야 하나 마스크에 관련된 클래스에 50_FPN만 존재하는 것을 확인.
backbone101은 import를 새로 하는 식으로 해결했으나 마스크는 따로 파이토치 내부에서 클래스로 선언했기에 방도를 찾다가 중지.
9) 훈련결과를 이용하여 과적합을 판단하는 방법을 설명하라.
작은 객체와 중간 크기 객체에서의 AP와 AR이 현저히 낮거나 -1로 표시되는 경우 모델이 특정 객체 크기나 특정 데이터 범주에 대해 잘 작동하지 않는다면, 해당 범주에서 과적합이 발생했거나 데이터가 부족한 경우이다.
위에 나온 결과는 훈련 데이터에서의 손실은 나오지 않았지만 훈련 데이터에서의 손실함수 값과 검증 데이터의 손실 값이 차이가 많이 나는 경우 과적합으로 판단한다.
10) 파이토치 예제에서 사용한 증식방법을 설명하고 증식결과영상를 출력하여 잘 동작하는지를 확인하라.
- 원본소스는 건드리지 말고 새로운 파일 my_train.py를 만들어서 테스트코드를 추가하여 실행할것
- 증식변환이 영상파일과 레이블정보에 모두 적용되었는지 확인할것, 예를 들어 영상을 회전변환을 적용했다면 레이블정보도 똑같은 회전변환이 적용되어 좌표값이 수정되어어야함
참고 : https://cafe.daum.net/SmartRobot/W2or/159
증식 관련 클래스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | class DetectionPresetTrain: # Note: this transform assumes that the input to forward() are always PIL # images, regardless of the backend parameter. def __init__( self, *, data_augmentation, hflip_prob=0.5, mean=(123.0, 117.0, 104.0), backend="pil", use_v2=False, ): T, tv_tensors = get_modules(use_v2) transforms = [] backend = backend.lower() if backend == "tv_tensor": transforms.append(T.ToImage()) elif backend == "tensor": transforms.append(T.PILToTensor()) elif backend != "pil": raise ValueError(f"backend can be 'tv_tensor', 'tensor' or 'pil', but got {backend}") if data_augmentation == "hflip": transforms += [T.RandomHorizontalFlip(p=hflip_prob)] elif data_augmentation == "lsj": transforms += [ T.ScaleJitter(target_size=(1024, 1024), antialias=True), # TODO: FixedSizeCrop below doesn't work on tensors! reference_transforms.FixedSizeCrop(size=(1024, 1024), fill=mean), T.RandomHorizontalFlip(p=hflip_prob), ] elif data_augmentation == "multiscale": transforms += [ T.RandomShortestSize(min_size=(480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800), max_size=1333), T.RandomHorizontalFlip(p=hflip_prob), ] elif data_augmentation == "ssd": fill = defaultdict(lambda: mean, {tv_tensors.Mask: 0}) if use_v2 else list(mean) transforms += [ T.RandomPhotometricDistort(), T.RandomZoomOut(fill=fill), T.RandomIoUCrop(), T.RandomHorizontalFlip(p=hflip_prob), ] elif data_augmentation == "ssdlite": transforms += [ T.RandomIoUCrop(), T.RandomHorizontalFlip(p=hflip_prob), ] else: raise ValueError(f'Unknown data augmentation policy "{data_augmentation}"') if backend == "pil": # Note: we could just convert to pure tensors even in v2. transforms += [T.ToImage() if use_v2 else T.PILToTensor()] transforms += [T.ToDtype(torch.float, scale=True)] if use_v2: transforms += [ T.ConvertBoundingBoxFormat(tv_tensors.BoundingBoxFormat.XYXY), T.SanitizeBoundingBoxes(), T.ToPureTensor(), ] self.transforms = T.Compose(transforms) def __call__(self, img, target): return self.transforms(img, target) | cs |
입력
1 2 3 4 | #Data Augmentaion Code. parser.add_argument( "--data-augmentation", default="hflip", type=str, help="data augmentation policy (default: hflip)" ) | cs |
입력 argument에 따라서 DetectionPresetTrain클래스에 저장되어있는 코드 실행.
입력 단계에서 원하는 증식을 추가할수는 없고 DetectionPresetTrain코드에서 사용자 전용 데이터 증식을 추가해야 함.
실행 명령행 인자. (--data-augmentation custumCompose 부분이 원하는 증식을 추가하는 부분이다.)
| python my_train.py --data-path D:\pytorch_cuda\detection_data\dataset1 --dataset coco --model fasterrcnn_resnet50_fpn --device cuda --epochs 26 --lr-steps 16 22 --aspect-ratio-group-factor 3 --weights-backbone ResNet50_Weights.IMAGENET1K_V1 --data-augmentation custumCompose --output-dir D:\pytorch_cuda\detection_data\dataset1\output |
증식과 마스크 정상 출력. 마스크의 위치도 바뀐것을 확인할 수 있었다.
클래스가 하나일 때 하나만 출력 예시.
'''
11) Yolo모델을 이용하여 객체검출을 수행하라.
- 파이토치 프레임워크로 구현된 Yolo 최신 객체검출 모델의 소스를 찾아서 위 예제코드에 붙여보라
- 원본소스는 건드리지 말고 새로운 파일 yolo_train.py을 만들어서 사용할것
- Faster RCNN모델과 추론(예측)시간, 정확도를 비교하시오.
https://github.com/THU-MIG/yolov10
https://learnopencv.com/fine-tuning-yolov10/
'''

첫댓글 3) 테스트코드는 예제에 있는데 뭐가 문제인지?
10) 추가된 증식변환이 무엇인지 설명하고 회전변환을 추가하여 변환결과를 확인하시오