diff --git a/DataAugmentation/Info.txt b/DataAugmentation/Info.txt deleted file mode 100644 index a78ed74..0000000 --- a/DataAugmentation/Info.txt +++ /dev/null @@ -1,2 +0,0 @@ -Data aug 한 거임. 근데 아차차 -random seed 안 박고 돌려서 재현성 떨어질 수 있겠다는 생각이 든다. \ No newline at end of file diff --git a/DataAugmentation/Sbatch.sh b/DataAugmentation/Sbatch.sh deleted file mode 100644 index 5c4f05c..0000000 --- a/DataAugmentation/Sbatch.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/bash - -#SBATCH -J test-run -#SBATCH --gres=gpu:1 -#SBATCH --cpus-per-gpu=8 -#SBATCH --mem-per-gpu=29G -#SBATCH -p batch_ce_ugrad -#SBATCH -t 1-0 -#SBATCH -o logs/slurm-%A.out - -python3 augfun.py -exit 0 - - diff --git a/DataAugmentation/augfun.py b/DataAugmentation/augfun.py deleted file mode 100644 index 79953f9..0000000 --- a/DataAugmentation/augfun.py +++ /dev/null @@ -1,231 +0,0 @@ -import numpy as np -import cv2 -import os -from PIL import Image -import random -new_folder = "/data/hhe5361/downloads/MainGalaxy10/train" -ori_folder = "/data/hhe5361/downloads/Galaxy10/train" - -#salt and pepper noise 추가한 거 -def add_salt_and_pepper_noise(image, salt_prob = 0.007, pepper_prob = 0.007): - """ - 이미지에 salt and pepper 노이즈 추가. - :param image: 원본 이미지 (BGR 형식) - :param salt_prob: salt 노이즈의 확률 (0 ~ 1) - :param pepper_prob: pepper 노이즈의 확률 (0 ~ 1) - :return: 노이즈가 추가된 이미지 - """ - # 이미지 복사 - noisy_image = image.copy() - - # Salt 노이즈 추가 - salt_mask = np.random.rand(*image.shape[:2]) < salt_prob - noisy_image[salt_mask] = 255 # salt는 흰색 (255) - - # Pepper 노이즈 추가 - pepper_mask = np.random.rand(*image.shape[:2]) < pepper_prob - noisy_image[pepper_mask] = 0 # pepper는 검은색 (0) - - return noisy_image - -def create_center_weighted_mask(image_shape): - height, width = image_shape - Y, X = np.ogrid[:height, :width] - center_y, center_x = height//2, width//2 - - # 가우시안 마스크 생성 - mask = np.exp(-((X - center_x)**2 + (Y - center_y)**2) / (2.0 * (min(width, height)/4)**2)) - return mask - -def apply_center_brightness_weight(image): - # 가우시안 마스크 생성 - center_mask = create_center_weighted_mask(image.shape[:2]) - - gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) - - # 밝기 정규화 - normalized_brightness = gray / 255.0 - - # 가중치 적용 - weighted_image = image * center_mask[..., np.newaxis] - weighted_image = weighted_image * normalized_brightness[..., np.newaxis] - - return weighted_image.astype(np.uint8) - -def apply_median_and_gaussian_filters(image, median_kernel_size=3, gaussian_kernel_size=5, gaussian_sigma=1.5): - """ - 이미지에 Median Filter와 Gaussian Filter를 동시에 적용하는 함수 - :param image: 원본 이미지 (BGR 형식) - :param median_kernel_size: Median Filter의 커널 크기 (홀수 값) - :param gaussian_kernel_size: Gaussian Filter의 커널 크기 (홀수 값) - :param gaussian_sigma: Gaussian Filter의 시그마 값 - :return: 필터가 적용된 이미지 - """ - # Median Filter 적용 - median_filtered = cv2.medianBlur(image, median_kernel_size) - - # Gaussian Filter 적용 - gaussian_filtered = cv2.GaussianBlur(median_filtered, (gaussian_kernel_size, gaussian_kernel_size), gaussian_sigma) - - return gaussian_filtered - -def apply_speckle_noise(image, scale=0.2): - """ - 이미지에 speckle noise 추가 - :param image: 원본 이미지 (BGR 형식) - :param scale: 노이즈 크기 (0 ~ 1) - :return: 노이즈가 추가된 이미지 - """ - # Convert to float to apply speckle noise - image_float = image.astype(np.float32) / 255.0 - - # Generate speckle noise - noise = np.random.normal(loc=0.0, scale=scale, size=image_float.shape) - noisy_image = image_float + image_float * noise - - # Clip values to valid range [0, 1] - noisy_image = np.clip(noisy_image, 0, 1) - - # Convert back to uint8 - noisy_image_uint8 = (noisy_image * 255).astype(np.uint8) - - return noisy_image_uint8 - -def split_and_overlay(image): - - image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) - # 이미지 객체를 PIL Image로 변환 - image = Image.fromarray(image) - - # 이미지 크기 확인 - width, height = image.size - - # 각 블록의 크기 계산 - block_width = width // 3 - block_height = height // 3 - - # 9개의 블록으로 분할 - blocks = [] - for i in range(3): - for j in range(3): - left = j * block_width - upper = i * block_height - right = left + block_width - lower = upper + block_height - block = image.crop((left, upper, right, lower)) - blocks.append(block) - - overlays = [] - offsets = [(0, 0), (0, 1), (1, 0), (1, 1)] - - for dx, dy in offsets: - overlay = Image.new('RGB', (block_width * 2, block_height * 2)) - for i in range(2): - for j in range(2): - block_index = (i + dx) * 3 + (j + dy) - block = blocks[block_index] - overlay.paste(block, (j * block_width, i * block_height)) - overlays.append(overlay) - - return overlays - -def rgb_to_grayscale(image: np.ndarray) -> np.ndarray: - # Convert to grayscale using cv2 - grayscale_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) - return grayscale_image - -def random_erasing(image: np.ndarray) -> np.ndarray: - """ - Apply random erasing to the input image without considering the center. - - Args: - image (np.ndarray): Input image of shape (256, 256, 3). - - Returns: - np.ndarray: Image with random erasing applied. - """ - # 이미지 크기와 중심 좌표 - height, width = image.shape[:2] - - # 랜덤하게 지울 영역 크기 설정 (가로와 세로 각각 독립적으로 설정) - erase_width = random.randint(width // 16, width // 4) # 최소 1/16 ~ 최대 1/4 너비 - erase_height = random.randint(height // 16, height // 4) # 최소 1/16 ~ 최대 1/4 높이 - - # 랜덤 좌표 생성 (이미지 영역 내에서) - top_left_x = random.randint(0, width - erase_width) - top_left_y = random.randint(0, height - erase_height) - - # 랜덤하게 선택된 영역을 0(검정색)으로 설정 - image[top_left_y:top_left_y + erase_height, top_left_x:top_left_x + erase_width, :] = 0 - - return image - - -def makeImage(fold): - # 클래스별 폴더를 순회 - for class_folder in os.listdir(fold): - class_folder_path = os.path.join(fold, class_folder) - new_class_path = os.path.join(new_folder,class_folder) - print(f"current : {class_folder_path}") - print(f"new : {new_class_path}") - - if os.path.isdir(class_folder_path): # 폴더가 클래스 폴더일 때만 진행 - for filename in os.listdir(class_folder_path): - file_path = os.path.join(class_folder_path, filename) - - # 이미지를 BGR 형식으로 로드 - image = cv2.imread(file_path) - - # 이미지 overlay 저장 - overlays = split_and_overlay(image.copy()) - for idx, overlay in enumerate(overlays): - current_path = os.path.join(new_class_path, f"overlay{idx}_{filename}") - overlay.save(current_path, 'JPEG') - - # 1. Salt and Pepper 노이즈 추가 - noisy_image = add_salt_and_pepper_noise(image.copy()) - current_path = os.path.join(new_class_path, f"s&P_{filename}") - cv2.imwrite(current_path, noisy_image) - - # 2. Center Brightness Weight 적용 - weighted_image = apply_center_brightness_weight(image.copy()) - current_path = os.path.join(new_class_path, f"Centerweighted_{filename}") - cv2.imwrite(current_path, weighted_image) - - # 3. Median Filter와 Gaussian Filter 적용 - filtered_image = apply_median_and_gaussian_filters(image.copy()) - current_path = os.path.join(new_class_path, f"filtered_{filename}") - cv2.imwrite(current_path, filtered_image) - - #4. Speckle Noise 추가 - speckle_noise = apply_speckle_noise(image.copy()) - current_path = os.path.join(new_class_path, f"speckle_{filename}") - cv2.imwrite(current_path, speckle_noise) - - # 5. RGB to Grayscale - grayscale_image = rgb_to_grayscale(image.copy()) - current_path = os.path.join(new_class_path, f"grayscale_{filename}") - cv2.imwrite(current_path, grayscale_image) - - # 6. Random Erasing - erased_image = random_erasing(image.copy()) - current_path = os.path.join(new_class_path, f"erased_{filename}") - cv2.imwrite(current_path, erased_image) - - print("모든 이미지에 대해 전처리 완료.") - -#makeImage(ori_folder) - -# 클래스별 폴더를 순회하여 이미지 파일 개수 세기 -for class_folder in os.listdir(new_folder): - class_folder_path = os.path.join(new_folder, class_folder) - - if os.path.isdir(class_folder_path): # 폴더가 클래스 폴더일 때만 진행 - image_count = 0 - for filename in os.listdir(class_folder_path): - file_path = os.path.join(class_folder_path, filename) - - if filename.lower().endswith(('.png', '.jpg', '.jpeg')): # 이미지 파일만 처리 - image_count += 1 - - print(f"클래스 '{class_folder}' 폴더의 이미지 개수: {image_count}") diff --git a/DataAugmentation/logs/slurm-67596.out b/DataAugmentation/logs/slurm-67596.out deleted file mode 100644 index 1bfa070..0000000 --- a/DataAugmentation/logs/slurm-67596.out +++ /dev/null @@ -1,30 +0,0 @@ -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -모든 이미지에 대해 전처리 완료. -클래스 'Round_Smooth' 폴더의 이미지 개수: 7404 -클래스 'Merging' 폴더의 이미지 개수: 5188 -클래스 'Disturbed' 폴더의 이미지 개수: 3028 -클래스 'Edge-on_with_Bulge' 폴더의 이미지 개수: 5244 -클래스 'In-between_Round_Smooth' 폴더의 이미지 개수: 5676 -클래스 'Barred_Spiral' 폴더의 이미지 개수: 5720 -클래스 'Unbarred_Loose_Spiral' 폴더의 이미지 개수: 7360 -클래스 'Unbarred_Tight_Spiral' 폴더의 이미지 개수: 5120 -클래스 'Cigar_Shaped_Smooth' 폴더의 이미지 개수: 936 -클래스 'Edge-on_without_Bulge' 폴더의 이미지 개수: 3984 diff --git a/README.md b/README.md index f604446..d7b5d69 100644 --- a/README.md +++ b/README.md @@ -1,15 +1 @@ -# Don't Merge In Any Case!!!!!! -그것만은 하지마. ---- -# path.py -> dataset 경로 변경 필요 -# slurm batch file 재작성 필요 -# main.py에 model 불러오는 코드 작성 필요 -# util 은 건들 x , optimtarget.py는 hyperparameter , dataaug.py는 건들 o - ---- - -그냥 model 마다 브랜치 파서 기록할 것 - -같은 모델인 경우일 때만, optimizer, hyperparameter만 달리 했을 때 ... 최적의 acc에 대해 merge 수행할 것. - -readme에는 acc 기록 +cross validation diff --git a/dataaug.py b/dataaug.py deleted file mode 100644 index 6e197b0..0000000 --- a/dataaug.py +++ /dev/null @@ -1,46 +0,0 @@ -from torchvision import datasets, transforms -from torch.utils.data import DataLoader -from path import train_dir, val_dir, test_dir -from optimTarget import batch_size -import torch -import numpy as np -import random - -seed = 3334 -torch.manual_seed(seed) -torch.cuda.manual_seed(seed) -torch.cuda.manual_seed_all(seed) -torch.backends.cudnn.deterministic = True -torch.backends.cudnn.benchmark = False -np.random.seed(seed) -random.seed(seed) - -train_transform = transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), -]) - -val_transform = transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), - ]) - - -test_transform = transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) - ]) - - -train_data = datasets.ImageFolder(train_dir) -val_data = datasets.ImageFolder(val_dir) -test_data = datasets.ImageFolder(test_dir) - -train_data.transform = train_transform -val_data.transform = val_transform -test_data.transform = test_transform - -train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=4) -val_loader = DataLoader(val_data, batch_size=batch_size, shuffle=True, num_workers=4) -test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=4) - diff --git a/dataset.py b/dataset.py new file mode 100644 index 0000000..870fb0b --- /dev/null +++ b/dataset.py @@ -0,0 +1,57 @@ +from torchvision import datasets, transforms +from torch.utils.data import DataLoader, ConcatDataset +import torch +import numpy as np +import random + +# Seed 설정 +seed = 3334 +torch.manual_seed(seed) +torch.cuda.manual_seed(seed) +torch.cuda.manual_seed_all(seed) +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False +np.random.seed(seed) +random.seed(seed) + +# 데이터 변환 정의 +train_transform = transforms.Compose([ + transforms.RandomHorizontalFlip(p=0.5), # 50% 확률로 수평 뒤집기 + transforms.RandomVerticalFlip(p=0.5), # 50% 확률로 수직 뒤집기 + transforms.RandomRotation(degrees=15), # ±15도 내에서 랜덤 회전 + transforms.ToTensor(), # 텐서로 변환 + transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), # 정규화 +]) + +val_transform = transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), +]) + +test_transform = transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), +]) + +# Train 데이터 경로들 +train_dir_1 = "/data/coqls1229/repos/mip_res34/Galaxy10/train" +train_dir_2 = "/data/a7381pp/repos/mlip/Galaxy10/aug" + +# 데이터셋 로드 +train_data_1 = datasets.ImageFolder(train_dir_1, transform=train_transform) +train_data_2 = datasets.ImageFolder(train_dir_2, transform=train_transform) + +# 두 데이터셋을 합침 +combined_train_data = ConcatDataset([train_data_1, train_data_2]) + +# Validation 및 Test 데이터 로드 +val_dir = "/data/coqls1229/repos/mip_res34/Galaxy10/val" # 실제 경로로 변경 필요 +test_dir = "/data/coqls1229/repos/mip_res34/Galaxy10/test" # 실제 경로로 변경 필요 +val_data = datasets.ImageFolder(val_dir, transform=val_transform) +test_data = datasets.ImageFolder(test_dir, transform=test_transform) + +# DataLoader 생성 +batch_size = 32 # 필요한 배치 크기로 변경 +train_loader = DataLoader(combined_train_data, batch_size=batch_size, shuffle=True, num_workers=4) +val_loader = DataLoader(val_data, batch_size=batch_size, shuffle=True, num_workers=4) +test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=4) \ No newline at end of file diff --git a/main.py b/main.py index e70b650..3e6f194 100644 --- a/main.py +++ b/main.py @@ -1,37 +1,76 @@ -# importing all the libraries we need -import torch -from torch import nn, optim -from torch.optim import lr_scheduler -from optimTarget import learning_rate, training_epochs, schedule_steps -from utils import checkParams, train_model, evaluate - -device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") -print(device) - -model_ft = EfficientNet.from_name("efficientnet-b0") -test_model = EfficientNet.from_name("efficientnet-b0") - -num_ftrs = model_ft._fc.in_features -model_ft._fc = nn.Linear(num_ftrs, 10) -test_model._fc = nn.Linear(num_ftrs, 10) - -model_ft= model_ft.cuda() -test_model = test_model.cuda() - -checkParams(model_ft) - -model_ft = model_ft.to(device) - -# Optimization Loss function -criterion = nn.CrossEntropyLoss() - -optimizer_ft = optim.SGD(model_ft.parameters(), lr=learning_rate, momentum=0.9) - -exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=schedule_steps, gamma=0.1) - - -# Training the model -model_ft, epc, trn, val = train_model(model = model_ft, criterion= criterion, optimizer= optimizer_ft, scheduler= exp_lr_scheduler, device= device , num_epochs=training_epochs) - -# Evaluating the model -predictions = evaluate(test_model,device) +# importing all the libraries we need +import torch +from torch import nn, optim +from torch.optim import lr_scheduler +from sklearn.model_selection import KFold +from torch.utils.data import DataLoader, Subset +from utils import checkParams, train_model, evaluate +from resnet import ResNet34 +from dataset import combined_train_data, test_data + +# Device 설정 +device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") +print(device) + +# Hyperparameters +learning_rate = 0.01 +training_epochs = 50 +schedule_steps = 7 +batch_size = 32 +num_splits = 5 # K-Fold의 Fold 수 + +# Cross Validation Setup +kf = KFold(n_splits=num_splits, shuffle=True, random_state=3334) + +# Cross Validation Training Loop +fold_results = [] +for fold, (train_idx, val_idx) in enumerate(kf.split(combined_train_data)): + print(f"Starting Fold {fold + 1}/{num_splits}") + + # Train/Validation 데이터 분리 + train_subset = Subset(combined_train_data, train_idx) + val_subset = Subset(combined_train_data, val_idx) + train_loader = DataLoader(train_subset, batch_size=batch_size, shuffle=True, num_workers=4) + val_loader = DataLoader(val_subset, batch_size=batch_size, shuffle=False, num_workers=4) + dataloaders = {'train': train_loader, 'val': val_loader} + + # 모델 초기화 + model_ft = ResNet34(num_classes=10).to(device) + checkParams(model_ft) + + # Optimization Loss function + criterion = nn.CrossEntropyLoss() + optimizer_ft = optim.SGD(model_ft.parameters(), lr=learning_rate, momentum=0.9) + exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=schedule_steps, gamma=0.1) + + # Training the model + model_ft, epc, trn, val = train_model( + model=model_ft, + criterion=criterion, + optimizer=optimizer_ft, + scheduler=exp_lr_scheduler, + device=device, + dataloaders=dataloaders, # train과 val DataLoader 전달 + num_epochs=training_epochs, + ) + + # Fold 결과 저장 + fold_results.append({ + 'fold': fold + 1, + 'train_loss': trn[0], + 'train_acc': trn[1], + 'val_loss': val[0], + 'val_acc': val[1], + }) + +# Test 데이터 평가 +test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=4) +test_model = ResNet34(num_classes=10).to(device) +predictions = evaluate(test_model, device) + +# Cross Validation 결과 요약 +print("\nCross Validation Results:") +for result in fold_results: + print(f"Fold {result['fold']}: Best Val Acc: {max(result['val_acc']):.4f}") + +print("Done!") \ No newline at end of file diff --git a/optimTarget.py b/optimTarget.py index f1206db..78ff541 100644 --- a/optimTarget.py +++ b/optimTarget.py @@ -1,4 +1,4 @@ -training_epochs = 50 -schedule_steps = 25 -learning_rate = 0.01 -batch_size = 64 +training_epochs = 50 +schedule_steps = 25 +learning_rate = 0.01 +batch_size = 64 \ No newline at end of file diff --git a/path.py b/path.py index 617cfa2..9924576 100644 --- a/path.py +++ b/path.py @@ -1,4 +1,4 @@ -dataset_dir = '/data/hhe5361/downloads/Galaxy10/' -train_dir = dataset_dir + 'train' -val_dir = dataset_dir + 'val' -test_dir = dataset_dir + 'test' +dataset_dir = '/data/coqls1229/repos/mip_res34Galaxy10/' +train_dir = dataset_dir + 'train' +val_dir = dataset_dir + 'val' +test_dir = dataset_dir + 'test' \ No newline at end of file diff --git a/resnet.py b/resnet.py new file mode 100644 index 0000000..81b5ee3 --- /dev/null +++ b/resnet.py @@ -0,0 +1,112 @@ +from utils import * +from torch import nn +import torch.nn.functional as F + +def conv3x3(in_planes, out_planes, stride=1): + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False) + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, in_planes, planes, stride=1, dropout_rate=0.5): + super(BasicBlock, self).__init__() + self.conv1 = conv3x3(in_planes, planes, stride) + self.bn1 = nn.BatchNorm2d(planes) + self.conv2 = conv3x3(planes, planes) + self.bn2 = nn.BatchNorm2d(planes) + self.dropout = nn.Dropout(p=dropout_rate) # Dropout 추가 + + self.shortcut = nn.Sequential() + if stride != 1 or in_planes != self.expansion * planes: + self.shortcut = nn.Sequential( + nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(self.expansion * planes) + ) + + def forward(self, x): + out = F.relu(self.bn1(self.conv1(x))) + out = self.dropout(out) # Dropout 적용 + out = self.bn2(self.conv2(out)) + out += self.shortcut(x) + out = F.relu(out) + return out + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, in_planes, planes, stride=1, dropout_rate=0.5): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False) + self.bn1 = nn.BatchNorm2d(planes) + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(planes) + self.conv3 = nn.Conv2d(planes, self.expansion * planes, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(self.expansion * planes) + self.dropout = nn.Dropout(p=dropout_rate) # Dropout 추가 + + self.shortcut = nn.Sequential() + if stride != 1 or in_planes != self.expansion * planes: + self.shortcut = nn.Sequential( + nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(self.expansion * planes) + ) + + def forward(self, x): + out = F.relu(self.bn1(self.conv1(x))) + out = F.relu(self.bn2(self.conv2(out))) + out = self.dropout(out) # Dropout 적용 + out = self.bn3(self.conv3(out)) + out += self.shortcut(x) + out = F.relu(out) + return out + +class ResNet(nn.Module): + def __init__(self, block, num_blocks, num_classes=10, dropout_rate=0.5): + super(ResNet, self).__init__() + self.in_planes = 64 + self.dropout = nn.Dropout(p=dropout_rate) # Dropout 추가 + + self.conv1 = conv3x3(3, 64) + self.bn1 = nn.BatchNorm2d(64) + self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=2) + self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2) + self.layer3 = self._make_layer(block, 128, num_blocks[2], stride=2) + self.layer4 = self._make_layer(block, 128, num_blocks[3], stride=2) + self.linear = nn.Linear(2048, num_classes) + + def _make_layer(self, block, planes, num_blocks, stride): + strides = [stride] + [1] * (num_blocks - 1) + layers = [] + for stride in strides: + layers.append(block(self.in_planes, planes, stride)) + self.in_planes = planes * block.expansion + return nn.Sequential(*layers) + + def forward(self, x): + out = F.relu(self.bn1(self.conv1(x))) + out = self.layer1(out) + out = self.layer2(out) + out = self.layer3(out) + out = self.layer4(out) + out = F.avg_pool2d(out, 4) + out = out.view(out.size(0), -1) + out = self.dropout(out) # Dropout 적용 + out = self.linear(out) + return out + + +def ResNet18(num_classes=10): + return ResNet(BasicBlock, [2,2,2,2], num_classes) + +def ResNet34(num_classes=10): + return ResNet(BasicBlock, [3,4,6,3], num_classes) + +def ResNet50(num_classes=10): + return ResNet(Bottleneck, [3,4,6,3], num_classes) + +def ResNet101(num_classes=10): + return ResNet(Bottleneck, [3,4,23,3], num_classes) + +def ResNet152(num_classes=10): + return ResNet(Bottleneck, [3,8,36,3], num_classes) \ No newline at end of file diff --git a/train.sh b/train.sh new file mode 100644 index 0000000..6382477 --- /dev/null +++ b/train.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +#SBATCH --job-name=data +#SBATCH --gres=gpu:1 +#SBATCH --cpus-per-gpu=8 +#SBATCH --mem-per-gpu=32G +#SBATCH --time=1-0 +#SBATCH --partition=batch_ugrad +#SBATCH -o slurm/%A.out + +python3 main.py +exit 0 \ No newline at end of file diff --git a/utils.py b/utils.py index 0da7e71..0a52ecf 100644 --- a/utils.py +++ b/utils.py @@ -1,152 +1,123 @@ -import sys -import numpy as np -import time -import torch -import copy -import itertools -from dataaug import train_loader, train_data, test_loader -import pandas as pd - -def checkParams(model): - pytorch_total_params = sum(p.numel() for p in model.parameters()) - print(f"Number of parameters: {pytorch_total_params}") - if int(pytorch_total_params) > 5000000: - print('Your model has the number of parameters more than 5 millions..') - sys.exit() - -def train_model(model, criterion, optimizer, scheduler, device, num_epochs=25): - since = time.time() - - best_model_wts = copy.deepcopy(model.state_dict()) - best_acc = 0.0 - - epoch_lst = [] - trn_loss_lst = [] - trn_acc_lst = [] - val_loss_lst = [] - val_acc_lst = [] - - for epoch in range(num_epochs): - print('-' * 50) - print('Epoch {}/{}'.format(epoch + 1, num_epochs)) - - # Each epoch has a training and validation phase - for phase in ['train', 'val']: - if phase == 'train': - model.train() # Set model to training mode - else: - model.eval() # Set model to evaluate mode - - running_loss = 0.0 - running_corrects = 0 - - # Iterate over data batches - for inputs, labels in train_loader: - inputs = inputs.to(device) - labels = labels.to(device) - - # zero the parameter gradients - optimizer.zero_grad() - - # forward - # track history if only in train - with torch.set_grad_enabled(phase == 'train'): - outputs = model(inputs) - _, preds = torch.max(outputs, 1) - loss = criterion(outputs, labels) - - # backward + optimize only if in training phase - if phase == 'train': - loss.backward() - optimizer.step() - - # statistics - running_loss += loss.item() * inputs.size(0) - running_corrects += torch.sum(preds == labels.data) - - if phase == 'train': - scheduler.step() - - epoch_acc = running_corrects.double() / len(train_data) - epoch_loss = running_loss / len(train_data) - - print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc)) - - # save training val metadata metrics - if phase == 'train': - - epoch_lst.append(epoch) - trn_loss_lst.append(np.round(epoch_loss,4 )) - trn_acc_lst.append(np.round(epoch_acc.cpu().item(),4 )) - - elif phase == 'val': - val_loss_lst.append(np.round(epoch_loss, 4)) - val_acc_lst.append(np.round(epoch_acc.cpu().item(), 4)) - - # deep copy the model - if phase == 'val' and epoch_acc > best_acc: - best_acc = epoch_acc - torch.save(model.state_dict(), 'model_best.pt') - best_model_wts = copy.deepcopy(model.state_dict()) - - torch.save(model.state_dict(),'model_latest.pt') - - trn_metadata = [trn_loss_lst, trn_acc_lst] - val_metadata = [val_loss_lst, val_acc_lst] - - time_elapsed = time.time() - since - print('Training complete in {:.0f}m {:.0f}s'.format( - time_elapsed // 60, time_elapsed % 60)) - print('Best val Acc: {:4f}'.format(best_acc)) - - # load best model weights - model.load_state_dict(best_model_wts) - return model, epoch_lst, trn_metadata, val_metadata - -def evaluate(test_model, device): - count = 0 - all_count = 0 - preds = [] - - # 저장경로는 변경하셔도 됩니다. - test_model.load_state_dict(torch.load('model_best.pt')) - test_model.to(device) # 모델을 GPU로 이동 - test_model.eval() # setting the model to evaluate mode - for inputs, labels in test_loader: - - #아래는 제출 시 삭제해야 할 코드임. - inputs = inputs.to(device) - labels = labels.to(device) - - # predicting - with torch.no_grad(): - outputs = test_model(inputs) - _, pred = torch.max(outputs, dim=1) # 예측값 - preds.append(pred.cpu()) # GPU -> CPU 이동 후 저장 - - # 정확도 계산 -> 삭제해야 할 부분 - count += (pred == labels).sum().item() - all_count += labels.size(0) # 현재 배치의 샘플 수 추가 - - # 최종 정확도 출력 - 삭제 해야 할 부분 - accuracy = count / all_count - print(f'Accuracy: {accuracy:.4f}') - - # 예측값 저장 - category = [t.numpy() for t in preds] - t_category = list(itertools.chain(*category)) - Id = list(range(0, len(t_category))) - - prediction = { - 'Id': Id, - 'Category': t_category - } - - prediction_df = pd.DataFrame(prediction, columns=['Id', 'Category']) - - # 저장경로는 변경하셔도 됩니다. - prediction_df.to_csv('prediction.csv', index=False) - - print('Done!!') - return preds - +import sys +import numpy as np +import time +import torch +import copy +import itertools +from torch.utils.data import DataLoader, Subset +from sklearn.model_selection import KFold +import pandas as pd + +def checkParams(model): + pytorch_total_params = sum(p.numel() for p in model.parameters()) + print(f"Number of parameters: {pytorch_total_params}") + if int(pytorch_total_params) > 5000000: + print('Your model has the number of parameters more than 5 millions..') + sys.exit() + +def train_model(model, criterion, optimizer, scheduler, dataloaders, device, num_epochs=25): + since = time.time() + + best_model_wts = copy.deepcopy(model.state_dict()) + best_acc = 0.0 + + epoch_lst = [] + trn_loss_lst = [] + trn_acc_lst = [] + val_loss_lst = [] + val_acc_lst = [] + + for epoch in range(num_epochs): + print('-' * 50) + print(f'Epoch {epoch + 1}/{num_epochs}') + + for phase in ['train', 'val']: + if phase == 'train': + model.train() + else: + model.eval() + + running_loss = 0.0 + running_corrects = 0 + + for inputs, labels in dataloaders[phase]: + inputs, labels = inputs.to(device), labels.to(device) + + optimizer.zero_grad() + with torch.set_grad_enabled(phase == 'train'): + outputs = model(inputs) + _, preds = torch.max(outputs, 1) + loss = criterion(outputs, labels) + + if phase == 'train': + loss.backward() + optimizer.step() + + running_loss += loss.item() * inputs.size(0) + running_corrects += torch.sum(preds == labels.data) + + epoch_loss = running_loss / len(dataloaders[phase].dataset) + epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset) + + print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}') + + if phase == 'train': + epoch_lst.append(epoch) + trn_loss_lst.append(round(epoch_loss, 4)) + trn_acc_lst.append(round(epoch_acc.cpu().item(), 4)) + scheduler.step() + else: + val_loss_lst.append(round(epoch_loss, 4)) + val_acc_lst.append(round(epoch_acc.cpu().item(), 4)) + + if epoch_acc > best_acc: + best_acc = epoch_acc + torch.save(model.state_dict(), 'model_best.pt') + best_model_wts = copy.deepcopy(model.state_dict()) + + torch.save(model.state_dict(), 'model_latest.pt') + + time_elapsed = time.time() - since + print(f'Training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s') + print(f'Best val Acc: {best_acc:.4f}') + + model.load_state_dict(best_model_wts) + return model, epoch_lst, [trn_loss_lst, trn_acc_lst], [val_loss_lst, val_acc_lst] + +def evaluate(test_model, test_loader, device): + count = 0 + all_count = 0 + preds = [] + + test_model.load_state_dict(torch.load('model_best.pt')) + test_model.to(device) + test_model.eval() + + for inputs, labels in test_loader: + inputs, labels = inputs.to(device), labels.to(device) + + with torch.no_grad(): + outputs = test_model(inputs) + _, pred = torch.max(outputs, dim=1) + preds.append(pred.cpu()) + + count += (pred == labels).sum().item() + all_count += labels.size(0) + + accuracy = count / all_count + print(f'Accuracy: {accuracy:.4f}') + + category = [t.numpy() for t in preds] + t_category = list(itertools.chain(*category)) + Id = list(range(0, len(t_category))) + + prediction = { + 'Id': Id, + 'Category': t_category + } + + prediction_df = pd.DataFrame(prediction, columns=['Id', 'Category']) + prediction_df.to_csv('prediction.csv', index=False) + + print('Done!!') + return preds \ No newline at end of file