|
|
https://github.com/dusty-nv/jetson-inference/blob/master/examples/segnet/segnet.cpp
24.08.13 : 검증 데이터셋을 보는 중 검증 데이터 셋에는 의자 클래스와 책상 클래스를 가진 검증 데이터셋이 극소수인것을 발견 유리문을 포함한 검증 데이터는 하나도 없음을 발견. 해당 데이터셋의 수량의 문제가 학습은 잘 되었으나 검증 데이터셋에서 약간만 틀어져도 확률이 낮아질 수 있다고 판단.
ㄴ의자와 책상 객체가 포함되어있는 데이터셋을 추가하고 난 이후에도 잘 인식되지 않음.
ㄴ 24.08.14 : 의자와 책상 등등의 물체들을 모두 blockage 영역으로 처리하여 하나로 취급하고 floor, wall, ceiling, blockage, background 총 5개의 클래스로 데이터셋을 재구성하여 테스트 하기로 함.
가장 중요한 바닥 데이터가 nan값이 나와 확인 필요.
데이터 증식을 V1으로 수정한 결과, 데이터 증식을 V2를 사용할 때 바닥을 인식을 못하는 문제 확인.
| python my_train.py -j 6 --data-path D:\pytorch_cuda\segmentaion_data\dataset2 --lr 0.015 --dataset coco -b 4 --model deeplabv3_resnet50 --aux-loss --device cuda --epoch 30 --weights-backbone ResNet50_Weights.IMAGENET1K_V1 --output-dir D:\pytorch_cuda\segmentaion_data\dataset2\output |
클래스를 줄인만큼 모델의 무게를 줄여야 하나 싶어서 resnet101대신 resnet50으로 낮추고 V1기법을 사용함.
현재까지의 결과 중에서는 긍정적인 표본이 나옴.
위 설정과 동일하나 학습률만 0.015에서 0.01로 변경한 값.
[연구실 바닥 데이터셋을 추가 - train(50), val(25)]
V2 데이터 증강을 사용하면 여전히 바닥 값이 nan값이 나와 취소. - 학습 중간에 계속해서 문제 파악이 필요.
이후부터는 V1으로 클래스를 직접 추가하여 데이터 증식을 수행하도록 함.
마지막 데이터 증식 클래스 추가 현황.
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | import random import numpy as np import torch from torchvision import transforms as T from torchvision.transforms import functional as F from torchvision.transforms.functional import InterpolationMode def pad_if_smaller(img, size, fill=0): min_size = min(img.size) if min_size < size: ow, oh = img.size padh = size - oh if oh < size else 0 padw = size - ow if ow < size else 0 img = F.pad(img, (0, 0, padw, padh), fill=fill) return img class Compose: def __init__(self, transforms): self.transforms = transforms def __call__(self, image, target): for t in self.transforms: image, target = t(image, target) return image, target class RandomResize: def __init__(self, min_size, max_size=None): self.min_size = min_size if max_size is None: max_size = min_size self.max_size = max_size def __call__(self, image, target): size = random.randint(self.min_size, self.max_size) image = F.resize(image, size, antialias=True) target = F.resize(target, size, interpolation=T.InterpolationMode.NEAREST) return image, target class RandomHorizontalFlip: def __init__(self, flip_prob): self.flip_prob = flip_prob def __call__(self, image, target): if random.random() < self.flip_prob: image = F.hflip(image) target = F.hflip(target) return image, target class RandomCrop: def __init__(self, size): self.size = size def __call__(self, image, target): image = pad_if_smaller(image, self.size) target = pad_if_smaller(target, self.size, fill=255) crop_params = T.RandomCrop.get_params(image, (self.size, self.size)) image = F.crop(image, *crop_params) target = F.crop(target, *crop_params) return image, target class CenterCrop: def __init__(self, size): self.size = size def __call__(self, image, target): image = F.center_crop(image, self.size) target = F.center_crop(target, self.size) return image, target class PILToTensor: def __call__(self, image, target): image = F.pil_to_tensor(image) target = torch.as_tensor(np.array(target), dtype=torch.int64) return image, target class ToDtype: def __init__(self, dtype, scale=False): self.dtype = dtype self.scale = scale def __call__(self, image, target): if not self.scale: return image.to(dtype=self.dtype), target image = F.convert_image_dtype(image, self.dtype) return image, target class Normalize: def __init__(self, mean, std): self.mean = mean self.std = std def __call__(self, image, target): image = F.normalize(image, mean=self.mean, std=self.std) return image, target class RandomRotations: def __init__(self, degrees=10 , interpolation=InterpolationMode.NEAREST, expand=False, center=None, fill=0): self.degrees = degrees self.interpolation = interpolation self.expand = expand self.center = center self.fill = fill def __call__(self, image, target): if random.random() < 0.5: self.degrees = -(self.degrees) image = F.rotate(image, self.degrees, self.interpolation, self.expand, self.center, self.fill) target = F.rotate(target, self.degrees, self.interpolation, self.expand, self.center, self.fill) return image, target class ColorJitter: def __init__(self, brightness=(0.7, 1.1), hue=(-0.2, 0.2), contrast=(0.5, 1.0), saturation=(0.9, 1.0)): self.brightness = brightness self.hue = hue self.contrast = contrast self.saturation = saturation def chose_number(self, value): random_value = random.uniform(value[0], value[1]) rounded_value = round(random_value, 1) return rounded_value def __call__(self, image, target): brightness = self.chose_number(self.brightness) saturation = self.chose_number(self.saturation) hue = self.chose_number(self.hue) contrast = self.chose_number(self.contrast) image = F.adjust_brightness(image, brightness) image = F.adjust_saturation(image, saturation) image = F.adjust_hue(image, hue) image = F.adjust_contrast(image, contrast) return image, target class RandomGrayscale: def __init__(self, p=0.1): self.p = p def __call__(self, image, target): num_output_channels, _, _ = F.get_dimensions(image) if torch.rand(1) < self.p: return F.rgb_to_grayscale(image, num_output_channels=num_output_channels), target return image, target class RandomErasing: def __init__(self, p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0): self.p = p self.scale = scale self.ratio = ratio self.value = value def chose_number(self, value): random_value = random.uniform(value[0], value[1]) rounded_value = round(random_value, 2) return rounded_value def __call__(self, image, target): scale = self.chose_number(value=self.scale) ratio = self.chose_number(value=self.ratio) transform = T.ToTensor() tensor_image = transform(image) #tensor_target = transform(target) img_c, img_h, img_w = tensor_image.shape image_height = img_h image_width = img_w #height and width erase_height = image_height * scale erase_width = image_width * scale #start x and y erase_height = int(erase_height) erase_width = int(erase_height) if image_width > erase_width: x = random.randint(0, image_width - erase_width) else: x = 0 if image_height > erase_height: y = random.randint(0, image_height - erase_height) else: y = 0 if random.random() <= self.p: image = transform(image) target = transform(target) image = F.erase(image, x, y, erase_height, erase_width, self.value) target = F.erase(target, x, y, erase_height, erase_width, self.value) transform = T.ToPILImage() image = transform(image) target = transform(target) return image, target class GaussianBlur: pass | cs |
| python my_train.py -j 6 --data-path D:\pytorch_cuda\segmentaion_data\dataset3 --lr 0.015 --dataset coco -b 4 --model deeplabv3_resnet50 --aux-loss --device cuda --epoch 30 --weights-backbone ResNet50_Weights.IMAGENET1K_V1 --output-dir D:\pytorch_cuda\segmentaion_data\dataset3\output |
resnet50이 아닌 101 사용
| python my_train.py -j 6 --data-path D:\pytorch_cuda\segmentaion_data\dataset3 --lr 0.015 --dataset coco -b 4 --model deeplabv3_resnet101 --aux-loss --device cuda --epoch 30 --weights-backbone ResNet101_Weights.IMAGENET1K_V1 --output-dir D:\pytorch_cuda\segmentaion_data\dataset3\output |
fransforms.py 코드
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | import random import numpy as np import torch from torchvision import transforms as T from torchvision.transforms import functional as F from torchvision.transforms.functional import InterpolationMode def pad_if_smaller(img, size, fill=0): min_size = min(img.size) if min_size < size: ow, oh = img.size padh = size - oh if oh < size else 0 padw = size - ow if ow < size else 0 img = F.pad(img, (0, 0, padw, padh), fill=fill) return img class Compose: def __init__(self, transforms): self.transforms = transforms def __call__(self, image, target): for t in self.transforms: image, target = t(image, target) return image, target class RandomResize: def __init__(self, min_size, max_size=None): self.min_size = min_size if max_size is None: max_size = min_size self.max_size = max_size def __call__(self, image, target): size = random.randint(self.min_size, self.max_size) image = F.resize(image, size, antialias=True) target = F.resize(target, size, interpolation=T.InterpolationMode.NEAREST) return image, target class RandomHorizontalFlip: def __init__(self, flip_prob): self.flip_prob = flip_prob def __call__(self, image, target): if random.random() < self.flip_prob: image = F.hflip(image) target = F.hflip(target) return image, target class RandomCrop: def __init__(self, size): self.size = size def __call__(self, image, target): image = pad_if_smaller(image, self.size) target = pad_if_smaller(target, self.size, fill=255) crop_params = T.RandomCrop.get_params(image, (self.size, self.size)) image = F.crop(image, *crop_params) target = F.crop(target, *crop_params) return image, target class CenterCrop: def __init__(self, size): self.size = size def __call__(self, image, target): image = F.center_crop(image, self.size) target = F.center_crop(target, self.size) return image, target class PILToTensor: def __call__(self, image, target): image = F.pil_to_tensor(image) target = torch.as_tensor(np.array(target), dtype=torch.int64) return image, target class ToDtype: def __init__(self, dtype, scale=False): self.dtype = dtype self.scale = scale def __call__(self, image, target): if not self.scale: return image.to(dtype=self.dtype), target image = F.convert_image_dtype(image, self.dtype) return image, target class Normalize: def __init__(self, mean, std): self.mean = mean self.std = std def __call__(self, image, target): image = F.normalize(image, mean=self.mean, std=self.std) return image, target class RandomRotations: def __init__(self, degrees=10 , interpolation=InterpolationMode.NEAREST, expand=False, center=None, fill=0): self.degrees = degrees self.interpolation = interpolation self.expand = expand self.center = center self.fill = fill def __call__(self, image, target): if random.random() < 0.5: self.degrees = -(self.degrees) image = F.rotate(image, self.degrees, self.interpolation, self.expand, self.center, self.fill) target = F.rotate(target, self.degrees, self.interpolation, self.expand, self.center, self.fill) return image, target class ColorJitter: def __init__(self, brightness=(0.7, 1.1), hue=(-0.2, 0.2), contrast=(0.5, 1.0), saturation=(0.9, 1.0)): self.brightness = brightness self.hue = hue self.contrast = contrast self.saturation = saturation def chose_number(self, value): random_value = random.uniform(value[0], value[1]) rounded_value = round(random_value, 1) return rounded_value def __call__(self, image, target): brightness = self.chose_number(self.brightness) saturation = self.chose_number(self.saturation) hue = self.chose_number(self.hue) contrast = self.chose_number(self.contrast) image = F.adjust_brightness(image, brightness) image = F.adjust_saturation(image, saturation) image = F.adjust_hue(image, hue) image = F.adjust_contrast(image, contrast) return image, target class RandomGrayscale: def __init__(self, p=0.1): self.p = p def __call__(self, image, target): num_output_channels, _, _ = F.get_dimensions(image) if torch.rand(1) < self.p: return F.rgb_to_grayscale(image, num_output_channels=num_output_channels), target return image, target | 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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | import torch from PIL import Image, ImageOps import torchvision.transforms as T import numpy as np import cv2 import time # Define the helper function def decode_segmap(image, nc=5): label_colors = np.array([(0, 0, 0), # 0=background # 1=ceiling, 2=floor, 3=wall, 4=blockage, 5=glassdoor (100, 100, 100), (255, 255, 255), (50, 50, 50), (0, 0, 0), (128, 0, 128), ]) r = np.zeros_like(image).astype(np.uint8) g = np.zeros_like(image).astype(np.uint8) b = np.zeros_like(image).astype(np.uint8) for l in range(0, nc): idx = image == l r[idx] = label_colors[l, 0] g[idx] = label_colors[l, 1] b[idx] = label_colors[l, 2] rgb = np.stack([r, g, b], axis=2) return rgb # 모델 로드 model = torch.load('D:\\pytorch_cuda\\segmentaion_data\\dataset3\\output\\model.pth', weights_only=False) model.eval() # 비디오 경로 및 캡처 객체 정의 video_path = 'D:/pytorch_cuda/segmentaion_data/dataset3/test/test.mp4' cap = cv2.VideoCapture(video_path) if not cap.isOpened(): print("Error: Cannot open video file") exit() # 비디오 속성 가져오기 fps = cap.get(cv2.CAP_PROP_FPS) frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 출력 비디오 작성을 위한 코덱 및 VideoWriter 객체 정의 output_path = 'D:/pytorch_cuda/segmentaion_data/dataset3/test/test_segmentation.mp4' fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width * 2, frame_height)) # 각 프레임 처리 while cap.isOpened(): ret, frame = cap.read() if not ret: break # OpenCV 이미지(BGR)를 PIL 이미지(RGB)로 변환 pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) # 이미지 전처리 pil_img = ImageOps.exif_transpose(pil_img) transform = T.Compose([ T.ToTensor(), T.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) img = transform(pil_img).unsqueeze(0).to('cuda') # 모델 출력 가져오기 with torch.no_grad(): out = model(img)['out'] om = torch.argmax(out.squeeze(), dim=0).detach().cpu().numpy() # 세그멘테이션 맵 디코딩 rgb = decode_segmap(om) # RGB를 BGR로 변환하여 OpenCV에 저장 output_frame = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) # 원본 및 세그멘테이션 프레임 나란히 연결 combined_frame = np.hstack((frame, output_frame)) # 프레임 표시 cv2.imshow('Original and Segmentation', combined_frame) # 키 입력 대기, 'q' 키를 누르면 종료 if cv2.waitKey(1) & 0xFF == ord('q'): break # 출력 비디오 파일에 작성 #out.write(combined_frame) # 비디오 캡처 및 기록기 해제 cap.release() out.release() cv2.destroyAllWindows() | cs |
jetson nano에 달려있는 카메라의 픽셀수가 적어 추론 시간이 낮은 대신 Decoder과정에서 세세하게 분류가 안되는 현상 발견.
현재 사이즈를 유지하려면 이전에 라벨링했던 데이터들을 제거하고 jetson nano로만 찍은 데이터셋을 구성하는 방안도 고려.
위 동영상은 (640, 360) 의 사이즈
학습한 이후 덮어씌워지는 모델들을 버리지 말고 옮겨 하이퍼 파라미터값과 학습 결과를 따로 보관하기로 함.
TensorRT 설치
1. 해당 사이트에서 NVIDIA CUDA Toolkit 다운로드 CUDA버전을 12.5가 아니라면 해당 버전으로 다운로드를 수행해야 함.
2. 해당 사이트에서 TensorRT 다운로드 윈도우즈 버전을 사용하며 OS환경과 CUDA 버전에 따라 다운로드를 수행
https://developer.nvidia.com/tensorrt/download/10x
3. Visual studio에서 경로 설정
링커 -> 입력 -> 추가 종속성
| nvinfer.lib nvinfer_plugin.lib nvonnxparser.lib nvparsers.lib cublas.lib cuda.lib cudart.lib cudnn.lib |
pytorch(python)환경에서 생성된 pth 확장자를 가진 model.pth를 onnx확장자로 변환시켜 C++환경에서 활동할 수 있게 만든 모델을 TensorRT가 받아 최적화 과정을 통해 하나의 엔진으로 변환한다.
이후 onnx파일 대신 engine확장자를 가진 model.engine 파일을 사용하여 추론을 진행한다.
(pth확장자를 onnx로 변환하는 코드는 기술 자료 참조.)
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | #include <NvInfer.h> #include <NvOnnxParser.h> #include <cuda_runtime_api.h> #include <iostream> #include <fstream> #include <chrono> #include <vector> #include <NvInferRuntimeCommon.h> using namespace nvinfer1; using namespace nvonnxparser; // Logger 클래스 정의 (TensorRT 로깅을 위해 사용) class Logger : public nvinfer1::ILogger { void log(Severity severity, const char* msg) noexcept override { // INFO 레벨이 아닌 로그만 출력 if (severity != Severity::kINFO) { std::cout << msg << std::endl; } } }; //엔진 파일생성(onnx->tensorrt변환) int main() { // TensorRT Logger 생성 Logger trtLogger; // TensorRT 빌더 생성 IBuilder* builder = createInferBuilder(trtLogger); if (!builder) { std::cerr << "TensorRT 빌더 생성 실패." << std::endl; return -1; } // 네트워크 정의 생성 INetworkDefinition* network = builder->createNetworkV2(0U); if (!network) { std::cerr << "TensorRT 네트워크 생성 실패." << std::endl; return -1; } // ONNX 파서를 사용해 모델 로드 IParser* parser = createParser(*network, trtLogger); if (!parser) { std::cerr << "TensorRT 파서 생성 실패." << std::endl; return -1; } // ONNX 모델을 파싱 if (!parser->parseFromFile("model.onnx", static_cast<int>(ILogger::Severity::kWARNING))) { std::cerr << "ONNX 모델 파싱 실패." << std::endl; return -1; } // 빌더 설정 생성 IBuilderConfig* config = builder->createBuilderConfig(); if (!config) { std::cerr << "TensorRT 빌더 설정 생성 실패." << std::endl; return -1; } const char* inputTensorName = network->getInput(0)->getName(); // 첫 번째 입력 텐서의 이름 가져오기 // 최적화 프로필 설정 추가 auto profile = builder->createOptimizationProfile(); profile->setDimensions(inputTensorName, OptProfileSelector::kMIN, Dims4{ 1, 3, 256, 256 }); // 최소 크기 profile->setDimensions(inputTensorName, OptProfileSelector::kOPT, Dims4{ 1, 3, 256, 256 }); // 최적 크기 profile->setDimensions(inputTensorName, OptProfileSelector::kMAX, Dims4{ 1, 3, 256, 256 }); // 최대 크기 config->addOptimizationProfile(profile); // 워크스페이스 메모리 크기 설정 (1GB) config->setMemoryPoolLimit(MemoryPoolType::kWORKSPACE, 1 << 30); // 네트워크를 직렬화하여 엔진 생성 IHostMemory* serializedModel = builder->buildSerializedNetwork(*network, *config); if (!serializedModel) { std::cerr << "TensorRT 엔진 직렬화 실패." << std::endl; return -1; } // 엔진을 파일로 저장 std::ofstream engineFile("model.engine", std::ios::binary); if (!engineFile) { std::cerr << "엔진 파일 쓰기 실패." << std::endl; return -1; } engineFile.write(reinterpret_cast<const char*>(serializedModel->data()), serializedModel->size()); engineFile.close(); std::cout << "엔진 파일이 model.engine으로 저장되었습니다." << std::endl; // 엔진을 바로 사용하기 위해 역직렬화 (옵션) IRuntime* runtime = createInferRuntime(trtLogger); ICudaEngine* engine = runtime->deserializeCudaEngine(serializedModel->data(), serializedModel->size()); if (!engine) { std::cerr << "TensorRT 엔진 생성 실패." << std::endl; return -1; } return 0; } | cs |
생성된 엔진.
[TensorRT 10.3에 대한 가이드 사이트]
https://docs.nvidia.com/deeplearning/tensorrt/api/c_api/index.html
https://scalar.tistory.com/173
https://www.eswcontest.or.kr/main.php
|
|

첫댓글 동영상 재생 안됨
tensorrt 사용과정 자세히 설명할것 onnx모델변환하여 하는것인지? engine파일을 어떻게 실행하는건지? 추론은 어떻게 하는건지?
tensorrt 사용시외 미사용시의 추론시간 비교할것