commit c64abc420670b77ddd7de9fd9a750ce285492b70 Author: pinb Date: Fri May 10 09:18:12 2024 +0900 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba98c42 --- /dev/null +++ b/README.md @@ -0,0 +1,1001 @@ +--- +layout: default +title: 20. 지능화 파일럿 프로젝트 +subtitle: Deep Learning +--- +----- + +[PINBlog Gitea Repository](https://gitea.pinblog.codes/CBNU/20_Final_Project) + +----- + +# 지능화 파일럿 프로젝트 - UNet으로 이차전지극판 영역분할 +- 산업인공지능학과 대학원 + 2022254026 + 김홍열 + + +--- + + +### UNet 논문 +[UNet](https://arxiv.org/pdf/1505.04597.pdf) + + +# 프로젝트 목표 +* UNet 모델을 학습하여 이차전지극판 영상에서 극판부분을 영역분할 + + +# 특이사항 +* Dataset은 기업보안으로 인해 반출 불가 +* 조명 밝기 변화를 이미지 별로 저장하였으므로 관련 Argumentation은 제외 +* GPU 메모리 한계로 512x512 크기로 리사이즈하여 학습 + + +# 데이터셋 +* 이차전지 업체에서 확보한 샘플을 12M 카메라로, Ring, Bar 조명 변화별 영상 촬영 +* Gold, Silver 극판별 각 9개의 모양을 정의 및 마스크 생성 +* Argumentation: Normalization(mean=0.5, std=0.5), RandomFlip(), Rotate(angle_range=(-90, 90)) +* 이미지 크기: 5120x5120x1 +* 학습데이터 수: 13638 +* 검증데이터 수: 3896 +* 테스트데이터 수: 1949 +* 총 데이터 수: 19483 + +--- + +# 프로젝트 코드 + +
+Load Dataset +
+ + +``` python + +import os +from glob import glob +import numpy as np +import torch +from torch.utils.data import Dataset +from PIL import Image +import matplotlib.pyplot as plt +from torchvision import transforms, datasets +import random +import cv2 + +class CustomDataset(Dataset): + def __init__(self, list_imgs, list_masks, transform=None): + self.list_imgs = list_imgs + self.list_masks = list_masks + self.transform = transform + + def __len__(self): + return len(self.list_imgs) + + def __getitem__(self, index): + img_path = self.list_imgs[index] + mask_path = self.list_masks[index] + + img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) + mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) + + # 이미지 크기를 512x512로 변경 + img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR) + mask = cv2.resize(mask, (512, 512), interpolation=cv2.INTER_NEAREST) + + img = img.astype(np.float32) / 255.0 + mask = mask.astype(np.float32) / 255.0 + + if img.ndim == 2: + img = img[:, :, np.newaxis] + if mask.ndim == 2: + mask = mask[:, :, np.newaxis] + + data = {'input': img, 'label': mask} + + if self.transform: + data = self.transform(data) + + return data + +def create_datasets(img_dir, mask_dir, train_ratio=0.7, val_ratio=0.2, transform=None): + list_imgs = sorted(glob(os.path.join(img_dir, '**', '*.png'), recursive=True)) + list_masks = sorted(glob(os.path.join(mask_dir, '**', '*.png'), recursive=True)) + + combined = list(zip(list_imgs, list_masks)) + random.shuffle(combined) + list_imgs, list_masks = zip(*combined) + + num_imgs = len(list_imgs) + num_train = int(num_imgs * train_ratio) + num_val = int(num_imgs * val_ratio) + + train_set = CustomDataset(list_imgs[:num_train], list_masks[:num_train], transform) + val_set = CustomDataset(list_imgs[num_train:num_train + num_val], list_masks[num_train:num_train + num_val], transform) + test_set = CustomDataset(list_imgs[num_train + num_val:], list_masks[num_train + num_val:], transform) + + return train_set, val_set, test_set + + + +``` + +
+
+ +
+Test DatasetLoader +
+ + +``` python + +# 사용 예시 +dir_img = 'C:/Users/pinb/Desktop/imgs' +dir_mask = 'C:/Users/pinb/Desktop/masks' +train_set, val_set, test_set = create_datasets(dir_img, dir_mask) + +# 첫 번째 이미지 확인 (예: train set) +data = train_set.__getitem__(7777) # 이미지 불러오기 + +input_img = data['input'] +label = data['label'] + +# 이미지 시각화 +plt.subplot(121) +plt.imshow(input_img.reshape(input_img.shape[0], input_img.shape[1]), cmap='gray') +plt.title('Input Image') + +plt.subplot(122) +plt.imshow(label.reshape(label.shape[0], label.shape[1]), cmap='gray') +plt.title('Label') + +plt.show() + +``` + +![output](./images/output1.png) + +
+
+ + +
+Argumentation +
+ + +``` python + +# 트렌스폼 구현하기 +class ToTensor(object): + # def __call__(self, data): + # label, input = data['label'], data['input'] + + # label = label.transpose((2, 0, 1)).astype(np.float32) + # input = input.transpose((2, 0, 1)).astype(np.float32) + + # data = {'label': torch.from_numpy(label), 'input': torch.from_numpy(input)} + + # return data + def __call__(self, data): + label, input = data['label'], data['input'] + + # 이미지가 이미 그레이스케일이면 채널 차원 추가 + if label.ndim == 2: + label = label[:, :, np.newaxis] + if input.ndim == 2: + input = input[:, :, np.newaxis] + + # 채널을 첫 번째 차원으로 이동 + label = label.transpose((2, 0, 1)).astype(np.float32) + input = input.transpose((2, 0, 1)).astype(np.float32) + + data = {'label': torch.from_numpy(label), 'input': torch.from_numpy(input)} + + return data + +class Normalization(object): + def __init__(self, mean=0.5, std=0.5): + self.mean = mean + self.std = std + + def __call__(self, data): + label, input = data['label'], data['input'] + + input = (input - self.mean) / self.std + + data = {'label': label, 'input': input} + + return data + +class RandomFlip(object): + def __call__(self, data): + label, input = data['label'], data['input'] + + if np.random.rand() > 0.5: + label = np.fliplr(label) + input = np.fliplr(input) + + if np.random.rand() > 0.5: + label = np.flipud(label) + input = np.flipud(input) + + data = {'label': label, 'input': input} + + return data + +# class Resize(object): +# def __init__(self, output_size): +# assert isinstance(output_size, (int, tuple)) +# self.output_size = output_size + +# def __call__(self, data): +# label, input = data['label'], data['input'] + +# h, w = input.shape[:2] +# if isinstance(self.output_size, int): +# if h > w: +# new_h, new_w = self.output_size * h / w, self.output_size +# else: +# new_h, new_w = self.output_size, self.output_size * w / h +# else: +# new_h, new_w = self.output_size + +# new_h, new_w = int(new_h), int(new_w) + +# input = cv2.resize(input, (new_w, new_h)) +# label = cv2.resize(label, (new_w, new_h)) + +# return {'label': label, 'input': input} + +class Rotate(object): + def __init__(self, angle_range): + assert isinstance(angle_range, (tuple, list)) and len(angle_range) == 2 + self.angle_min, self.angle_max = angle_range + + def __call__(self, data): + label, input = data['label'], data['input'] + + # NumPy 배열로 변환 (필요한 경우) + if not isinstance(input, np.ndarray): + input = np.array(input) + if not isinstance(label, np.ndarray): + label = np.array(label) + + # (H, W, C) 형태를 (H, W)로 변경 (필요한 경우) + if input.ndim == 3 and input.shape[2] == 1: + input = input.squeeze(2) + if label.ndim == 3 and label.shape[2] == 1: + label = label.squeeze(2) + + # 랜덤 각도 선택 및 회전 적용 + angle = np.random.uniform(self.angle_min, self.angle_max) + h, w = input.shape[:2] + center = (w / 2, h / 2) + rot_matrix = cv2.getRotationMatrix2D(center, angle, 1.0) + input = cv2.warpAffine(input, rot_matrix, (w, h)) + label = cv2.warpAffine(label, rot_matrix, (w, h)) + + return {'label': label, 'input': input} + +# class Crop(object): +# def __init__(self, output_size): +# assert isinstance(output_size, (int, tuple)) +# if isinstance(output_size, int): +# self.output_size = (output_size, output_size) +# else: +# assert len(output_size) == 2 +# self.output_size = output_size + +# def __call__(self, data): +# label, input = data['label'], data['input'] + +# h, w = input.shape[:2] +# new_h, new_w = self.output_size + +# top = np.random.randint(0, h - new_h) +# left = np.random.randint(0, w - new_w) + +# input = input[top: top + new_h, left: left + new_w] +# label = label[top: top + new_h, left: left + new_w] + +# return {'label': label, 'input': input} + + + +``` + +
+
+ + +
+Test Argumentation +
+ + +``` python + +# 트랜스폼 잘 구현되었는지 확인 +transform = transforms.Compose([Normalization(mean=0.5, std=0.5), RandomFlip(), Rotate(angle_range=(-90, 90)), ToTensor()]) + +dir_img = 'C:/Users/pinb/Desktop/imgs' +dir_mask = 'C:/Users/pinb/Desktop/masks' +train_set, val_set, test_set = create_datasets(img_dir=dir_img, mask_dir=dir_mask, transform=transform) + + +data = train_set.__getitem__(12599) # 한 이미지 불러오기 +input = data['input'] +label = data['label'] + +# 불러온 이미지 시각화 +plt.subplot(122) +plt.hist(label.flatten(), bins=20) +plt.title('label') + +plt.subplot(121) +plt.hist(input.flatten(), bins=20) +plt.title('input') + +# 이미지 시각화 +plt.subplot(121) +plt.imshow(input.squeeze(), cmap='gray') +plt.title('Input Image') + +plt.subplot(122) +plt.imshow(label.squeeze(), cmap='gray') +plt.title('Label') + +plt.tight_layout() +plt.show() + +``` + +![output](./images/output2.png) + +
+
+ +--- + +
+Model Network +
+ + +``` python + +## 라이브러리 불러오기 +import os +import numpy as np + +import torch +import torch.nn as nn +from torch.utils.data import DataLoader +from torch.utils.tensorboard import SummaryWriter + +import matplotlib.pyplot as plt + +## 네트워크 구축하기 +class UNet(nn.Module): + def __init__(self): + super(UNet, self).__init__() + + # Convolution + BatchNormalization + Relu 정의하기 + def CBR2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=True): + layers = [] + layers += [nn.Conv2d(in_channels=in_channels, out_channels=out_channels, + kernel_size=kernel_size, stride=stride, padding=padding, + bias=bias)] + layers += [nn.BatchNorm2d(num_features=out_channels)] + layers += [nn.ReLU()] + + cbr = nn.Sequential(*layers) + + return cbr + + # 수축 경로(Contracting path) + self.enc1_1 = CBR2d(in_channels=1, out_channels=64) + self.enc1_2 = CBR2d(in_channels=64, out_channels=64) + + self.pool1 = nn.MaxPool2d(kernel_size=2) + + self.enc2_1 = CBR2d(in_channels=64, out_channels=128) + self.enc2_2 = CBR2d(in_channels=128, out_channels=128) + + self.pool2 = nn.MaxPool2d(kernel_size=2) + + self.enc3_1 = CBR2d(in_channels=128, out_channels=256) + self.enc3_2 = CBR2d(in_channels=256, out_channels=256) + + self.pool3 = nn.MaxPool2d(kernel_size=2) + + self.enc4_1 = CBR2d(in_channels=256, out_channels=512) + self.enc4_2 = CBR2d(in_channels=512, out_channels=512) + + self.pool4 = nn.MaxPool2d(kernel_size=2) + + self.enc5_1 = CBR2d(in_channels=512, out_channels=1024) + + # 확장 경로(Expansive path) + self.dec5_1 = CBR2d(in_channels=1024, out_channels=512) + + self.unpool4 = nn.ConvTranspose2d(in_channels=512, out_channels=512, + kernel_size=2, stride=2, padding=0, bias=True) + + self.dec4_2 = CBR2d(in_channels=2 * 512, out_channels=512) + self.dec4_1 = CBR2d(in_channels=512, out_channels=256) + + self.unpool3 = nn.ConvTranspose2d(in_channels=256, out_channels=256, + kernel_size=2, stride=2, padding=0, bias=True) + + self.dec3_2 = CBR2d(in_channels=2 * 256, out_channels=256) + self.dec3_1 = CBR2d(in_channels=256, out_channels=128) + + self.unpool2 = nn.ConvTranspose2d(in_channels=128, out_channels=128, + kernel_size=2, stride=2, padding=0, bias=True) + + self.dec2_2 = CBR2d(in_channels=2 * 128, out_channels=128) + self.dec2_1 = CBR2d(in_channels=128, out_channels=64) + + self.unpool1 = nn.ConvTranspose2d(in_channels=64, out_channels=64, + kernel_size=2, stride=2, padding=0, bias=True) + + self.dec1_2 = CBR2d(in_channels=2 * 64, out_channels=64) + self.dec1_1 = CBR2d(in_channels=64, out_channels=64) + + self.fc = nn.Conv2d(in_channels=64, out_channels=1, kernel_size=1, stride=1, padding=0, bias=True) + + # self.enc1_1 = CBR2d(in_channels=1, out_channels=64) + # self.pool1 = nn.MaxPool2d(kernel_size=2) + + # self.enc2_1 = CBR2d(in_channels=64, out_channels=128) + # self.pool2 = nn.MaxPool2d(kernel_size=2) + + # self.enc3_1 = CBR2d(in_channels=128, out_channels=256) + # self.pool3 = nn.MaxPool2d(kernel_size=2) + + # self.enc4_1 = CBR2d(in_channels=256, out_channels=512) + # self.pool4 = nn.MaxPool2d(kernel_size=2) + + # self.enc5_1 = CBR2d(in_channels=512, out_channels=1024) + + # # 확장 경로(Expansive path)의 깊이 감소 + # self.dec5_1 = CBR2d(in_channels=1024, out_channels=512) + # self.unpool4 = nn.ConvTranspose2d(in_channels=512, out_channels=512, kernel_size=2, stride=2) + + # self.dec4_1 = CBR2d(in_channels=512 + 512, out_channels=256) + # self.unpool3 = nn.ConvTranspose2d(in_channels=256, out_channels=256, kernel_size=2, stride=2) + + # self.dec3_1 = CBR2d(in_channels=256 + 256, out_channels=128) + # self.unpool2 = nn.ConvTranspose2d(in_channels=128, out_channels=128, kernel_size=2, stride=2) + + # self.dec2_1 = CBR2d(in_channels=128 + 128, out_channels=64) + # self.unpool1 = nn.ConvTranspose2d(in_channels=64, out_channels=64, kernel_size=2, stride=2) + + # self.dec1_1 = CBR2d(in_channels=64 + 64, out_channels=64) + # self.fc = nn.Conv2d(in_channels=64, out_channels=1, kernel_size=1, stride=1, padding=0, bias=True) + + # forward 함수 정의하기 + def forward(self, x): + enc1_1 = self.enc1_1(x) + enc1_2 = self.enc1_2(enc1_1) + pool1 = self.pool1(enc1_2) + + enc2_1 = self.enc2_1(pool1) + enc2_2 = self.enc2_2(enc2_1) + pool2 = self.pool2(enc2_2) + + enc3_1 = self.enc3_1(pool2) + enc3_2 = self.enc3_2(enc3_1) + pool3 = self.pool3(enc3_2) + + enc4_1 = self.enc4_1(pool3) + enc4_2 = self.enc4_2(enc4_1) + pool4 = self.pool4(enc4_2) + + enc5_1 = self.enc5_1(pool4) + + dec5_1 = self.dec5_1(enc5_1) + + unpool4 = self.unpool4(dec5_1) + cat4 = torch.cat((unpool4, enc4_2), dim=1) + dec4_2 = self.dec4_2(cat4) + dec4_1 = self.dec4_1(dec4_2) + + unpool3 = self.unpool3(dec4_1) + cat3 = torch.cat((unpool3, enc3_2), dim=1) + dec3_2 = self.dec3_2(cat3) + dec3_1 = self.dec3_1(dec3_2) + + unpool2 = self.unpool2(dec3_1) + cat2 = torch.cat((unpool2, enc2_2), dim=1) + dec2_2 = self.dec2_2(cat2) + dec2_1 = self.dec2_1(dec2_2) + + unpool1 = self.unpool1(dec2_1) + cat1 = torch.cat((unpool1, enc1_2), dim=1) + dec1_2 = self.dec1_2(cat1) + dec1_1 = self.dec1_1(dec1_2) + + x = self.fc(dec1_1) + + # enc1_1 = self.enc1_1(x) + # pool1 = self.pool1(enc1_1) + + # enc2_1 = self.enc2_1(pool1) + # pool2 = self.pool2(enc2_1) + + # enc3_1 = self.enc3_1(pool2) + # pool3 = self.pool3(enc3_1) + + # enc4_1 = self.enc4_1(pool3) + # pool4 = self.pool4(enc4_1) + + # enc5_1 = self.enc5_1(pool4) + + # dec5_1 = self.dec5_1(enc5_1) + + # unpool4 = self.unpool4(dec5_1) + # cat4 = torch.cat((unpool4, enc4_1), dim=1) + # dec4_1 = self.dec4_1(cat4) + + # unpool3 = self.unpool3(dec4_1) + # cat3 = torch.cat((unpool3, enc3_1), dim=1) + # dec3_1 = self.dec3_1(cat3) + + # unpool2 = self.unpool2(dec3_1) + # cat2 = torch.cat((unpool2, enc2_1), dim=1) + # dec2_1 = self.dec2_1(cat2) + + # unpool1 = self.unpool1(dec2_1) + # cat1 = torch.cat((unpool1, enc1_1), dim=1) + # dec1_1 = self.dec1_1(cat1) + + # x = self.fc(dec1_1) + + return x + +``` + +
+
+ + +
+Model Load & Save +
+ + +``` python + +## 네트워크 저장하기 +def save(ckpt_dir, net, optim, epoch): + if not os.path.exists(ckpt_dir): + os.makedirs(ckpt_dir) + + torch.save({'net': net.state_dict(), 'optim': optim.state_dict()}, + "%s/model_epoch%d.pth" % (ckpt_dir, epoch)) + +## 네트워크 불러오기 +def load(ckpt_dir, net, optim): + if not os.path.exists(ckpt_dir): + epoch = 0 + return net, optim, epoch + + ckpt_lst = os.listdir(ckpt_dir) + ckpt_lst.sort(key=lambda f: int(''.join(filter(str.isdigit, f)))) + + dict_model = torch.load('%s/%s' % (ckpt_dir, ckpt_lst[-1])) + + net.load_state_dict(dict_model['net']) + optim.load_state_dict(dict_model['optim']) + epoch = int(ckpt_lst[-1].split('epoch')[1].split('.pth')[0]) + + return net, optim, epoch + +``` + +
+
+ +--- + +
+Train +
+ + +``` python + +torch.cuda.empty_cache() +os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:128' + + +``` + + +``` python + +dir_img = 'C:/Users/pinb/Desktop/imgs' +dir_mask = 'C:/Users/pinb/Desktop/masks' +transform = transforms.Compose([Normalization(mean=0.5, std=0.5), RandomFlip(), Rotate(angle_range=(-90, 90)), ToTensor()]) +train_set, val_set, test_set = create_datasets(img_dir=dir_img, mask_dir=dir_mask, transform=transform) + + +``` + + +``` python + +# 훈련 파라미터 설정하기 +lr = 1e-3 +batch_size = 4 +num_epoch = 10 + +base_dir = './2nd_Battery/unet' +ckpt_dir = os.path.join(base_dir, "checkpoint") +log_dir = os.path.join(base_dir, "log") + +# 네트워크 생성하기 +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +net = UNet().to(device) + +# 손실함수 정의하기 +fn_loss = nn.BCEWithLogitsLoss().to(device) + +# Optimizer 설정하기 +optim = torch.optim.Adam(net.parameters(), lr=lr) + +# 그 밖에 부수적인 functions 설정하기 +fn_tonumpy = lambda x: x.to('cpu').detach().numpy().transpose(0, 2, 3, 1) +fn_denorm = lambda x, mean, std: (x * std) + mean +fn_class = lambda x: 1.0 * (x > 0.5) + + +``` + + +``` python + +# 훈련을 위한 Transform과 DataLoader +loader_train = DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=0) +loader_val = DataLoader(val_set, batch_size=batch_size, shuffle=False, num_workers=0) + +# 그밖에 부수적인 variables 설정하기 +num_data_train = len(train_set) +num_data_val = len(val_set) + +num_batch_train = np.ceil(num_data_train / batch_size) +num_batch_val = np.ceil(num_data_val / batch_size) + + +# Tensorboard 를 사용하기 위한 SummaryWriter 설정 +writer_train = SummaryWriter(log_dir=os.path.join(log_dir, 'train')) +writer_val = SummaryWriter(log_dir=os.path.join(log_dir, 'val')) + +# 네트워크 학습시키기 +st_epoch = 0 +# 학습한 모델이 있을 경우 모델 로드하기 +net, optim, st_epoch = load(ckpt_dir=ckpt_dir, net=net, optim=optim) + +for epoch in range(st_epoch + 1, num_epoch + 1): + net.train() + loss_arr = [] + + for batch, data in enumerate(loader_train, 1): + # forward pass + label = data['label'].to(device) + input = data['input'].to(device) + + output = net(input) + + # backward pass + optim.zero_grad() + + loss = fn_loss(output, label) + loss.backward() + + optim.step() + + # 손실함수 계산 + loss_arr += [loss.item()] + + print("TRAIN: EPOCH %04d / %04d | BATCH %04d / %04d | LOSS %.4f" % + (epoch, num_epoch, batch, num_batch_train, np.mean(loss_arr))) + + # Tensorboard 저장하기 + label = fn_tonumpy(label) + input = fn_tonumpy(fn_denorm(input, mean=0.5, std=0.5)) + output = fn_tonumpy(fn_class(output)) + + writer_train.add_image('label', label, num_batch_train * (epoch - 1) + batch, dataformats='NHWC') + writer_train.add_image('input', input, num_batch_train * (epoch - 1) + batch, dataformats='NHWC') + writer_train.add_image('output', output, num_batch_train * (epoch - 1) + batch, dataformats='NHWC') + + writer_train.add_scalar('loss', np.mean(loss_arr), epoch) + + with torch.no_grad(): + net.eval() + loss_arr = [] + + for batch, data in enumerate(loader_val, 1): + # forward pass + label = data['label'].to(device) + input = data['input'].to(device) + + output = net(input) + + # 손실함수 계산하기 + loss = fn_loss(output, label) + + loss_arr += [loss.item()] + + print("VALID: EPOCH %04d / %04d | BATCH %04d / %04d | LOSS %.4f" % + (epoch, num_epoch, batch, num_batch_val, np.mean(loss_arr))) + + # Tensorboard 저장하기 + label = fn_tonumpy(label) + input = fn_tonumpy(fn_denorm(input, mean=0.5, std=0.5)) + output = fn_tonumpy(fn_class(output)) + + writer_val.add_image('label', label, num_batch_val * (epoch - 1) + batch, dataformats='NHWC') + writer_val.add_image('input', input, num_batch_val * (epoch - 1) + batch, dataformats='NHWC') + writer_val.add_image('output', output, num_batch_val * (epoch - 1) + batch, dataformats='NHWC') + + writer_val.add_scalar('loss', np.mean(loss_arr), epoch) + + # epoch 5마다 모델 저장하기 + if epoch % 1 == 0: + save(ckpt_dir=ckpt_dir, net=net, optim=optim, epoch=epoch) + + writer_train.close() + writer_val.close() + + +``` + + +``` plaintext + +TRAIN: EPOCH 0010 / 0010 | BATCH 0001 / 3410 | LOSS 0.0049 +TRAIN: EPOCH 0010 / 0010 | BATCH 0002 / 3410 | LOSS 0.0055 +TRAIN: EPOCH 0010 / 0010 | BATCH 0003 / 3410 | LOSS 0.0054 +TRAIN: EPOCH 0010 / 0010 | BATCH 0004 / 3410 | LOSS 0.0052 +TRAIN: EPOCH 0010 / 0010 | BATCH 0005 / 3410 | LOSS 0.0051 +TRAIN: EPOCH 0010 / 0010 | BATCH 0006 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0007 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0008 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0009 / 3410 | LOSS 0.0051 +TRAIN: EPOCH 0010 / 0010 | BATCH 0010 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0011 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0012 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0013 / 3410 | LOSS 0.0049 +TRAIN: EPOCH 0010 / 0010 | BATCH 0014 / 3410 | LOSS 0.0049 +TRAIN: EPOCH 0010 / 0010 | BATCH 0015 / 3410 | LOSS 0.0049 +TRAIN: EPOCH 0010 / 0010 | BATCH 0016 / 3410 | LOSS 0.0049 +TRAIN: EPOCH 0010 / 0010 | BATCH 0017 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0018 / 3410 | LOSS 0.0049 +TRAIN: EPOCH 0010 / 0010 | BATCH 0019 / 3410 | LOSS 0.0049 +TRAIN: EPOCH 0010 / 0010 | BATCH 0020 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0021 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0022 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0023 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0024 / 3410 | LOSS 0.0050 +TRAIN: EPOCH 0010 / 0010 | BATCH 0025 / 3410 | LOSS 0.0049 +... +VALID: EPOCH 0010 / 0010 | BATCH 0971 / 0974 | LOSS 0.0113 +VALID: EPOCH 0010 / 0010 | BATCH 0972 / 0974 | LOSS 0.0113 +VALID: EPOCH 0010 / 0010 | BATCH 0973 / 0974 | LOSS 0.0113 +VALID: EPOCH 0010 / 0010 | BATCH 0974 / 0974 | LOSS 0.0113 + + +``` + + +
+
+ +--- + +
+Test +
+ + +``` python + +print('train set: ' + str(len(train_set))) +print('val set: ' + str(len(val_set))) +print('test set: ' + str(len(test_set))) +print('total: ' + str(len(train_set)+ len(val_set)+ len(test_set))) + + +``` + + +``` plaintext + +train set: 13638 +val set: 3896 +test set: 1949 +total: 19483 + + +``` + + +``` python + +loader_test = DataLoader(test_set, batch_size=batch_size, shuffle=False, num_workers=0) + +# 그밖에 부수적인 variables 설정하기 +num_data_test = len(test_set) +num_batch_test = np.ceil(num_data_test / batch_size) + +# 결과 디렉토리 생성하기 +result_dir = os.path.join(base_dir, 'result') +if not os.path.exists(result_dir): + os.makedirs(os.path.join(result_dir, 'png')) + os.makedirs(os.path.join(result_dir, 'numpy')) + + +net, optim, st_epoch = load(ckpt_dir=ckpt_dir, net=net, optim=optim) + +with torch.no_grad(): + net.eval() + loss_arr = [] + + for batch, data in enumerate(loader_test, 1): + # forward pass + label = data['label'].to(device) + input = data['input'].to(device) + + output = net(input) + + # 손실함수 계산하기 + loss = fn_loss(output, label) + + loss_arr += [loss.item()] + + print("TEST: BATCH %04d / %04d | LOSS %.4f" % + (batch, num_batch_test, np.mean(loss_arr))) + + # Tensorboard 저장하기 + label = fn_tonumpy(label) + input = fn_tonumpy(fn_denorm(input, mean=0.5, std=0.5)) + output = fn_tonumpy(fn_class(output)) + + # 테스트 결과 저장하기 + for j in range(label.shape[0]): + id = num_batch_test * (batch - 1) + j + + plt.imsave(os.path.join(result_dir, 'png', 'label_%04d.png' % id), label[j].squeeze(), cmap='gray') + plt.imsave(os.path.join(result_dir, 'png', 'input_%04d.png' % id), input[j].squeeze(), cmap='gray') + plt.imsave(os.path.join(result_dir, 'png', 'output_%04d.png' % id), output[j].squeeze(), cmap='gray') + + np.save(os.path.join(result_dir, 'numpy', 'label_%04d.npy' % id), label[j].squeeze()) + np.save(os.path.join(result_dir, 'numpy', 'input_%04d.npy' % id), input[j].squeeze()) + np.save(os.path.join(result_dir, 'numpy', 'output_%04d.npy' % id), output[j].squeeze()) + +print("AVERAGE TEST: BATCH %04d / %04d | LOSS %.4f" % + (batch, num_batch_test, np.mean(loss_arr))) + + +``` + +``` plaintext + +TEST: BATCH 0001 / 0488 | LOSS 0.0084 +TEST: BATCH 0002 / 0488 | LOSS 0.0077 +TEST: BATCH 0003 / 0488 | LOSS 0.0105 +TEST: BATCH 0004 / 0488 | LOSS 0.0095 +TEST: BATCH 0005 / 0488 | LOSS 0.0094 +TEST: BATCH 0006 / 0488 | LOSS 0.0093 +TEST: BATCH 0007 / 0488 | LOSS 0.0092 +TEST: BATCH 0008 / 0488 | LOSS 0.0094 +TEST: BATCH 0009 / 0488 | LOSS 0.0094 +TEST: BATCH 0010 / 0488 | LOSS 0.0127 +TEST: BATCH 0011 / 0488 | LOSS 0.0126 +TEST: BATCH 0012 / 0488 | LOSS 0.0124 +TEST: BATCH 0013 / 0488 | LOSS 0.0123 +TEST: BATCH 0014 / 0488 | LOSS 0.0121 +TEST: BATCH 0015 / 0488 | LOSS 0.0118 +TEST: BATCH 0016 / 0488 | LOSS 0.0116 +TEST: BATCH 0017 / 0488 | LOSS 0.0114 +TEST: BATCH 0018 / 0488 | LOSS 0.0114 +TEST: BATCH 0019 / 0488 | LOSS 0.0113 +TEST: BATCH 0020 / 0488 | LOSS 0.0111 +TEST: BATCH 0021 / 0488 | LOSS 0.0110 +TEST: BATCH 0022 / 0488 | LOSS 0.0108 +TEST: BATCH 0023 / 0488 | LOSS 0.0106 +TEST: BATCH 0024 / 0488 | LOSS 0.0107 +TEST: BATCH 0025 / 0488 | LOSS 0.0106 +... +TEST: BATCH 0486 / 0488 | LOSS 0.0109 +TEST: BATCH 0487 / 0488 | LOSS 0.0109 +TEST: BATCH 0488 / 0488 | LOSS 0.0109 +AVERAGE TEST: BATCH 0488 / 0488 | LOSS 0.0109 + + +``` + + +
+
+ + + +
+Result +
+ + +``` python + +## +lst_data = os.listdir(os.path.join(result_dir, 'numpy')) + +lst_label = [f for f in lst_data if f.startswith('label')] +lst_input = [f for f in lst_data if f.startswith('input')] +lst_output = [f for f in lst_data if f.startswith('output')] + +lst_label.sort() +lst_input.sort() +lst_output.sort() + +## +id = 0 + +label = np.load(os.path.join(result_dir,"numpy", lst_label[id])) +input = np.load(os.path.join(result_dir,"numpy", lst_input[id])) +output = np.load(os.path.join(result_dir,"numpy", lst_output[id])) + +## 플롯 그리기 +plt.figure(figsize=(8,6)) +plt.subplot(131) +plt.imshow(input, cmap='gray') +plt.title('input') + +plt.subplot(132) +plt.imshow(label, cmap='gray') +plt.title('label') + +plt.subplot(133) +plt.imshow(output, cmap='gray') +plt.title('output') + +plt.show() + + +``` + +![output](./images/output3.png) + +
+
+ +--- + +# 결과 + +
+Result +
+ +![Confusion Matrix](./images/confusionmatrix.png) +![F1-Curve](./images/f1score.png) +![PR-Curve](./images/prcurve.png) + +
+
+ +--- + +### 참고[¶]() + +- 지능화파일럿 과목, 윤성철 교수 +- ChatGPT +- [DACON-UNET](https://dacon.io/forum/405807?dtype=recent) \ No newline at end of file