|
1) 다음 2가지 방식으로 훈련결과를 실시간으로 시각화하시오.
코드에서 모든 파라미터를 변수에 직접 초기화하고 필요한 코드만 남기고 최대한 간단하게
훈련로스, 정확도, 검증로스, 검증정확도 그래프 출력기능을 다음 2가지 방식으로 구현
- 교재 626페이지 코드를 이용하는 방법, 텐서보드를 사용하여 시각화하는 방법
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 | import datetime import os import time import torch import torch.utils.data import torchvision import torchvision.transforms as transforms import utils from torch import nn from torch.utils.data.dataloader import default_collate from transforms import get_mixup_cutmix from torch.utils.tensorboard import SummaryWriter import matplotlib.pyplot as plt # 파라미터 초기화 data_path = 'C:/Users/AIRLAB/vision_workspace/pytorch/dataset1' output_dir = 'C:/Users/AIRLAB/Desktop/my_train/classification/outputs' batch_size = 32 epochs = 30 lr = 0.001 num_classes = 2 log_dir = os.path.join("runs", datetime.datetime.now().strftime("%Y%m%d-%H%M%S")) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 텐서보드 설정 writer = SummaryWriter(log_dir) # 데이터 변환 train_transforms = transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) val_transforms = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) # 데이터셋 및 데이터로더 train_dataset = torchvision.datasets.ImageFolder(os.path.join(data_path, 'train'), train_transforms) val_dataset = torchvision.datasets.ImageFolder(os.path.join(data_path, 'val'), val_transforms) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4) val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=4) # 모델 설정 model = torchvision.models.resnet50(weights=torchvision.models.ResNet50_Weights.IMAGENET1K_V1) # 출력층을 초기 설정하여 ImageNet 가중치로 초기화 model.fc = nn.Linear(model.fc.in_features, 1000) model = model.to(device) # 마지막 출력층을 실제 클래스의 갯수로 수정 model.fc = nn.Linear(model.fc.in_features, num_classes) model = model.to(device) print(model.fc) # 컨벌루션 레이어의 가중치 고정 for param in model.parameters(): param.requires_grad = False model.fc.weight.requires_grad = True model.fc.bias.requires_grad = True # 손실 함수 및 옵티마이저 criterion = nn.CrossEntropyLoss() optimizer = torch.optim.AdamW(model.fc.parameters(), lr=lr) # 시각화 코드 train_losses = [] val_losses = [] train_accuracies = [] val_accuracies = [] def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch): model.train() running_loss = 0.0 running_corrects1 = 0 num_samples = 0 start_time = time.time() for i, (inputs, labels) in enumerate(data_loader): inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() acc1, _ = utils.accuracy(outputs, labels, topk=(1, 2)) running_loss += loss.item() * inputs.size(0) running_corrects1 += acc1.item() * inputs.size(0) num_samples += inputs.size(0) if i % 10 == 0: print(f'Epoch [{epoch}/{epochs}] Step [{i}/{len(data_loader)}]: ' f'Loss: {running_loss / num_samples:.4f} Acc@1: {running_corrects1 / num_samples:.4f}') epoch_loss = running_loss / num_samples epoch_acc1 = running_corrects1 / num_samples epoch_time = time.time() - start_time print(f'Epoch [{epoch}/{epochs}] completed in {epoch_time:.2f}s - ' f'Loss: {epoch_loss:.4f} Acc@1: {epoch_acc1:.4f}') return epoch_loss, epoch_acc1 def evaluate(model, criterion, data_loader, device): model.eval() running_loss = 0.0 running_corrects1 = 0 num_samples = 0 start_time = time.time() with torch.inference_mode(): for inputs, labels in data_loader: inputs, labels = inputs.to(device), labels.to(device) outputs = model(inputs) loss = criterion(outputs, labels) acc1, _ = utils.accuracy(outputs, labels, topk=(1, 2)) running_loss += loss.item() * inputs.size(0) running_corrects1 += acc1.item() * inputs.size(0) num_samples += inputs.size(0) epoch_loss = running_loss / num_samples epoch_acc1 = running_corrects1 / num_samples epoch_time = time.time() - start_time print(f'Test completed in {epoch_time:.2f}s - ' f'Loss: {epoch_loss:.4f} Acc@1: {epoch_acc1:.4f}') return epoch_loss, epoch_acc1 if __name__ == '__main__': # 출력 디렉토리 생성 if not os.path.exists(output_dir): os.makedirs(output_dir) # 훈련 및 검증 루프 for epoch in range(epochs): print(f'Start training Epoch {epoch}/{epochs - 1}') print('-' * 10) train_loss, train_acc1 = train_one_epoch(model, criterion, optimizer, train_loader, device, epoch) val_loss, val_acc1 = evaluate(model, criterion, val_loader, device) train_losses.append(train_loss) train_accuracies.append(train_acc1) val_losses.append(val_loss) val_accuracies.append(val_acc1) print(f'Train Loss: {train_loss:.4f} Acc@1: {train_acc1:.4f}') print(f'Val Loss: {val_loss:.4f} Acc@1: {val_acc1:.4f}') writer.add_scalars('Loss', {'train': train_loss, 'val': val_loss}, epoch) writer.add_scalars('Accuracy@1', {'train': train_acc1, 'val': val_acc1}, epoch) # 모델 저장 torch.save(model.state_dict(), os.path.join(output_dir, "model.pth")) # 그래프 시각화 plt.figure(figsize=(12, 6)) plt.subplot(1, 2, 1) plt.plot(range(epochs), train_losses, label='Train Loss') plt.plot(range(epochs), val_losses, label='Val Loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.subplot(1, 2, 2) plt.plot(range(epochs), train_accuracies, label='Train Acc@1') plt.plot(range(epochs), val_accuracies, label='Val Acc@1') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show() writer.close() | cs |
2) 최종 모델파일을 이용하여 테스트하는 파이토치 코드 my_test.py를 작성하시오.
- 테스트영상은 cat, dog 각각 10장씩 인터넷에서 다운받아 사용할것
- 파일을 개별적으로 오픈하지 말고 파이썬 os 패키지를 이용하여 데이터 폴더명을 주면 폴더에 저장된 영상파일을 모두 검색하여 자동으로 불러와서 테스트하도록 할것
- 영상 1장의 추론(예측)시간을 측정하여 출력하시오.
- 정확도를 계산하여 출력하시오.
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 | import torch from PIL import Image # 이미지 처리를 위한 Pillow 라이브러리 임포트 import matplotlib.pyplot as plt import torchvision.transforms as transforms # 이미지 변환을 위한 torchvision.transforms 임포트 import torchvision.models as models import numpy as np import cv2 import os # 운영 체제 관련 기능을 위한 os 라이브러리 임포트 import time class_label = ['cat', 'dog'] # 클래스 레이블 정의 (예: 고양이, 개) test_folder = r'C:\Users\Admin\LEE_workspace\pytorch\dataset1\test' # 테스트 이미지 경로 model_path = r'D:\Users\Admin\Desktop\my_train\classification\model.pth' # 모델 파일 경로 # 모델 구조 정의 ResNet50사용 def initialize_model(num_classes, use_pretrained=True): weights = models.ResNet50_Weights.IMAGENET1K_V1 if use_pretrained else None model_ft = models.resnet50(weights=weights) num_ftrs = model_ft.fc.in_features model_ft.fc = torch.nn.Linear(num_ftrs, num_classes) # 출력 레이어 크기 조정 (클래스 개수에 맞게) return model_ft # 클래스 레이블 수에 맞춰 모델 초기화 num_classes = len(class_label) # 클래스 수 정의 (고양이, 개) model = initialize_model(num_classes) # 저장된 가중치 로드 model.load_state_dict(torch.load(model_path)) # 모델을 추론 모드로 설정 model.eval() # 이미지 변환 정의 transform = transforms.Compose([ # 여러 변환을 조합하여 하나의 변환으로 만듦 transforms.Resize(256), # 이미지 크기 조정 (256x256) transforms.CenterCrop(224), # 이미지 중앙을 224x224로 자르기 transforms.ToTensor(), # 이미지를 텐서(tensor)로 변환 transforms.Normalize( # 텐서 정규화 mean=[0.485, 0.456, 0.406], # 평균값 std=[0.229, 0.224, 0.225] # 표준편차 ) ]) def load_test_images(folder_path): total_correct = 0 # 정확하게 예측한 이미지 수 total_images = 0 # 총 테스트한 이미지 수 for filename in os.listdir(folder_path): # 폴더 내의 모든 파일에 대해 반복 if filename.endswith(".jpg") or filename.endswith(".jfif") or filename.endswith(".webp"): # 지원하는 파일 형식 검사 image_path = os.path.join(folder_path, filename) # 파일 경로 생성 test_image = Image.open(image_path) # 이미지 파일 열기 plt.figure(figsize=(5, 5)) # 그림 크기 설정 plt.imshow(test_image) # 이미지를 화면에 표시 img = transform(test_image) # 변환을 테스트 이미지에 적용 start_time = time.time() # 추론 시작 시간 기록 with torch.no_grad(): # 그래디언트 계산 비활성화 (추론 모드) pred = model(img.unsqueeze(0)) # 모델에 이미지를 입력하여 예측 수행 probabilities = torch.nn.functional.softmax(pred, dim=1) # 소프트맥스 함수 적용하여 확률 계산 y_pred = torch.argmax(probabilities) # 가장 높은 확률을 가진 클래스 인덱스 추출 confidence = probabilities[0][y_pred].item() * 100 # 확률을 백분율로 변환 end_time = time.time() # 추론 종료 시간 기록 # 예측 결과와 시간을 출력 print(f"Predicted: {class_label[y_pred]}, Confidence: {confidence:.2f}%, Time taken: {end_time - start_time:.4f} seconds") # 예측이 맞는지 확인 if ('cat' in filename and y_pred == 0) or ('dog' in filename and y_pred == 1): total_correct += 1 total_images += 1 # 예측 결과를 그림에 표시 plt.text(0, -10, f'{class_label[y_pred]}: {confidence:.2f}%', size=20, color='blue') # 예측된 클래스와 확률을 그림 위에 텍스트로 표시 plt.xticks([]) plt.yticks([]) plt.show() # 최종 정확도 계산 및 출력 accuracy = total_correct / total_images if total_images > 0 else 0 print(f"Accuracy: {accuracy:.4f}") # 데이터 폴더 내의 이미지 파일을 불러와서 테스트 load_test_images(test_folder) | cs |
3) 최종 모델파일을 이용하여 테스트하는 opencv C++ 코드 my_test.cpp를 작성하시오.
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 | #include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <filesystem>//C++17부터 지원 #include <iostream> #include <chrono> using namespace cv; using namespace cv::dnn; using namespace std; namespace fs = std::filesystem; vector<String> classNames = { "cat", "dog" }; Mat preprocess_image(const Mat& img) { Mat img_resized, img_float; resize(img, img_resized, Size(224, 224)); img_resized.convertTo(img_float, CV_32F, 1.0 / 255); Mat channels[3]; split(img_float, channels); // Normalize each channel channels[0] = (channels[0] - 0.485) / 0.229; channels[1] = (channels[1] - 0.456) / 0.224; channels[2] = (channels[2] - 0.406) / 0.225; merge(channels, 3, img_float); return img_float; } Mat mat_to_blob(const Mat& img) { Mat blob; dnn::blobFromImage(img, blob); return blob; } int main() { // 모델 로드 string model_path = "model.onnx"; Net net = readNet(model_path); if (net.empty()) { cerr << "Network load failed!" << endl; return -1; } string test_folder = "C:\\Users\\Admin\\LEE_workspace\\pytorch\\dataset1\\test"; // 테스트 이미지 폴더 경로 int total_images = 0; int correct_predictions = 0; for (const auto& entry : fs::directory_iterator(test_folder)) { if (entry.is_regular_file()) { string image_path = entry.path().string(); Mat img = imread(image_path); if (img.empty()) { cerr << "Could not open or find the image: " << image_path << endl; continue; } // 이미지 전처리 Mat img_preprocessed = preprocess_image(img); Mat img_blob = mat_to_blob(img_preprocessed); // 추론 시작 시간 기록 auto start = chrono::high_resolution_clock::now(); // 추론 net.setInput(img_blob); Mat prob = net.forward(); // 추론 종료 시간 기록 auto end = chrono::high_resolution_clock::now(); chrono::duration<double> elapsed = end - start; // 결과 확인 및 디스플레이 double maxVal; Point maxLoc; minMaxLoc(prob, NULL, &maxVal, NULL, &maxLoc); string label = entry.path().filename().string(); bool is_correct = (label.find("cat") != string::npos && maxLoc.x == 0) || (label.find("dog") != string::npos && maxLoc.x == 1); if (is_correct) { correct_predictions++; } total_images++; string str = format("%s (%4.2lf%%)", classNames[maxLoc.x].c_str(), maxVal * 100); putText(img, str, Point(10, 30), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 0, 255)); imshow("img", img); cout << "이미지: " << image_path << ", 예측: " << classNames[maxLoc.x] << ", 정확도: " << maxVal * 100 << "%" << ", 걸린시간: " << elapsed.count() << " seconds" << endl; waitKey(0); // 이미지 디스플레이를 유지 } } return 0; } | cs |
4) 전이학습이란 무엇인가?
전이학습은 기존에 학습된 모델을 새로운 작업에 재사용하는 기법이다. 일반적으로 대규모 데이터셋에서 사전 학습된 모델을 활용하여 작은 데이터셋에서의 모델 학습 성능을 향상시키는데 사용된다.
5) 전이학습을 하는 이유는 무엇인가?
사전 학습된 가중치를 사용하여 학습 시간을 크게 단축할 수 있다. 또한 대규모 데이터셋에서 학습된 모델을 사용함으로써 작은 데이터셋에서도 좋은 성능을 얻을 수 있으며 다양한 특징을 이미 학습하고 있어서 새로운 작업에서도 더 나은 일반화 성능을 기대할 수 있다.
6) 전이학습에서 훈련하는 웨이트는 컨벌루션레이어와 완전연결레이어중 어느것인가?
전이학습은 완전연결층을 훈련시키고 일반적으로 컨볼루션층은 고정한다. 완전연결층은 새로운 작업에 맞게 이 특징들을 조합하여 최종 예측을 수행하기 때문이다.
사전 학습된 모델의 컨볼루션층의 가중치를 고정하여 학습되지 않도록 설정하여 사전에 학습된 특징을 유지할 수 있게 하여 학습시간을 줄인다.
7) 파이토치 예제 train.py는 제품수준의 기능을 갖추고 있으므로 매우 복잡하여 코드 분석이 매우 어렵다. 아래 주소의 튜토리얼 예제를 참고하여 필요한 기능만 구현한 간결한 코드 my_train.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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, models, transforms import time import os import copy from datetime import datetime from torch.utils.tensorboard import SummaryWriter # 하이퍼파라미터 설정 data_dir = "C:/Users/AIRLAB/vision_workspace/pytorch/dataset1" model_name = "resnet50" num_classes = 2 batch_size = 32 num_epochs = 30 feature_extract = True learning_rate = 0.001 # 텐서보드 설정 log_dir = os.path.join("runs", datetime.now().strftime("%Y%m%d-%H%M%S")) writer = SummaryWriter(log_dir) # 모델 가중치 고정 함수 # Transfer Learning Tutorial의 set_parameter_requires_grad 함수 참고 def set_parameter_requires_grad(model, feature_extracting): if feature_extracting: for param in model.parameters(): param.requires_grad = False # 모델 초기화 함수 # Transfer Learning Tutorial의 initialize_model 함수 참고 def initialize_model(model_name, num_classes, feature_extract, use_pretrained=True): model_ft = None input_size = 0 if model_name == "resnet50": model_ft = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1 if use_pretrained else None) set_parameter_requires_grad(model_ft, feature_extract) num_ftrs = model_ft.fc.in_features model_ft.fc = nn.Linear(num_ftrs, num_classes) input_size = 224 return model_ft, input_size # 데이터 전처리 설정 # Transfer Learning Tutorial의 data_transforms 설정 참고 data_transforms = { 'train': transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), 'val': transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), } # 데이터셋 로드 # Transfer Learning Tutorial의 image_datasets 및 dataloaders 설정 참고 image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'val']} dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True, num_workers=4) for x in ['train', 'val']} dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']} class_names = image_datasets['train'].classes # 장치 설정 (GPU 사용 가능시 GPU, 아니면 CPU) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 모델 초기화 model_ft, input_size = initialize_model(model_name, num_classes, feature_extract, use_pretrained=True) # 모델을 GPU로 이동 model_ft = model_ft.to(device) # 손실 함수 설정 criterion = nn.CrossEntropyLoss() # 옵티마이저 설정 (feature_extract가 True인 경우 마지막 레이어의 파라미터만 전달) # Transfer Learning Tutorial의 optimizer 설정 참고 optimizer_ft = optim.AdamW(filter(lambda p: p.requires_grad, model_ft.parameters()), lr=learning_rate) # 학습률 스케줄러 설정 # Transfer Learning Tutorial의 학습률 스케줄러 설정 참고 exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1) # 모델 학습 및 검증 함수 # Transfer Learning Tutorial의 train_model 함수 참고 def train_model(model, dataloaders, criterion, optimizer, scheduler, num_epochs=25): since = time.time() best_model_wts = copy.deepcopy(model.state_dict()) best_acc = 0.0 for epoch in range(num_epochs): print(f'Epoch {epoch}/{num_epochs-1}') print('-' * 10) # 각 epoch마다 학습 및 검증 단계 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 = inputs.to(device) labels = labels.to(device) # 파라미터 그라디언트 초기화 optimizer.zero_grad() # forward # 학습일 때만 그라디언트 계산 추적 with torch.set_grad_enabled(phase == 'train'): outputs = model(inputs) _, preds = torch.max(outputs, 1) loss = criterion(outputs, labels) # 학습 단계인 경우 backward + 최적화 수행 if phase == 'train': loss.backward() optimizer.step() # 통계 running_loss += loss.item() * inputs.size(0) running_corrects += torch.sum(preds == labels.data) if phase == 'train': scheduler.step() epoch_loss = running_loss / dataset_sizes[phase] epoch_acc = running_corrects.double() / dataset_sizes[phase] print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}') # 텐서보드에 기록 writer.add_scalars('Loss', {phase: epoch_loss}, epoch) writer.add_scalars('Accuracy', {phase: epoch_acc}, epoch) # 모델 복사 if phase == 'val' and epoch_acc > best_acc: best_acc = epoch_acc best_model_wts = copy.deepcopy(model.state_dict()) print() 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 if __name__ == "__main__": # 모델 학습 model_ft = train_model(model_ft, dataloaders, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=num_epochs) # 모델 저장 torch.save(model_ft.state_dict(), os.path.join(log_dir, "model.pth")) # 텐서보드 기록 종료 writer.close() | cs |
8) 훈련결과를 이용하여 과적합을 판단하는 방법을 설명하라.
과적합이 발생하면 훈련 손실은 계속 감소하지만 검증 손실은 일정 지점이후 다시 증가한다 이는 훈련 데이터에 잘 맞춰져서 검증 데이터에 대한 일반화 성능이 떨어진다는 의미이다. 또는 훈련 정학도는 계속 증가하지만 검증 정확도는 일정지점 이후 감소하거나 더이상 증가하지 않으면 과적합이라고 판단 할 수있다.
예방 방법으로는 더 많은 데이터 수집,정규화 기법 사용(드롭아웃,L2정규화),데이터 증강,간단한 모델 사용 등이있다.
9) 파이토치 예제에서 사용한 증식방법을 설명하고 증식결과영상를 출력하여 잘 동작하는지를 확인하라
또 증식방법을 추가하거나 삭제하는 방법을 설명하고 원하는 증식방법을 추가하여 훈련을 진행해보라.
- 원본소스는 건드리지 말고 새로운 파일 my_train.py를 만들어서 테스트코드를 추가하여 실행할것
데이터 증식 방법은 transforms 모듈을 사용하여 증식 할 수 있다. 예제에서 사용한 대표적인 증식 방법은 이미지를 무작위로 자르고 크기를 224X224로 조정, 무작위로 좌우 반전,이미지의 각 채널을 정규화 하였다.
증식방법을 추가하거나 삭제하는 방법은 transforms의 compose내에서 새로운 증식 방법을 추가하거나 수정하면 된다.
명도,채도,밝기등을 추가로 증식 할 수 있다.
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 | import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, models, transforms import torchvision.utils as vutils import time import os import copy from datetime import datetime from torch.utils.tensorboard import SummaryWriter import matplotlib.pyplot as plt import numpy as np # 하이퍼파라미터 설정 data_dir = "C:/Users/AIRLAB/vision_workspace/pytorch/dataset1" model_name = "resnet50" num_classes = 2 batch_size = 32 num_epochs = 30 feature_extract = True learning_rate = 0.001 # 텐서보드 설정 log_dir = os.path.join("runs", datetime.now().strftime("%Y%m%d-%H%M%S")) writer = SummaryWriter(log_dir) # 모델 가중치 고정 함수 def set_parameter_requires_grad(model, feature_extracting): if feature_extracting: for param in model.parameters(): param.requires_grad = False # 모델 초기화 함수 def initialize_model(model_name, num_classes, feature_extract, use_pretrained=True): model_ft = None input_size = 0 if model_name == "resnet50": model_ft = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1 if use_pretrained else None) set_parameter_requires_grad(model_ft, feature_extract) num_ftrs = model_ft.fc.in_features model_ft.fc = nn.Linear(num_ftrs, num_classes) input_size = 224 return model_ft, input_size # 데이터 전처리 설정 data_transforms = { 'train': transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), 'val': transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), } # 데이터셋 로드 image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'val']} dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True, num_workers=4) for x in ['train', 'val']} dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']} class_names = image_datasets['train'].classes # 장치 설정 (GPU 사용 가능시 GPU, 아니면 CPU) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 모델 초기화 model_ft, input_size = initialize_model(model_name, num_classes, feature_extract, use_pretrained=True) # 모델을 GPU로 이동 model_ft = model_ft.to(device) # 마지막 레이어의 가중치만 학습하도록 설정 for param in model_ft.parameters(): param.requires_grad = False model_ft.fc.weight.requires_grad = True model_ft.fc.bias.requires_grad = True # 손실 함수 설정 criterion = nn.CrossEntropyLoss() # 옵티마이저 설정 (feature_extract가 True인 경우 마지막 레이어의 파라미터만 전달) optimizer_ft = optim.AdamW(filter(lambda p: p.requires_grad, model_ft.parameters()), lr=learning_rate) # 학습률 스케줄러 설정 exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1) # 증식된 이미지 시각화 함수 def imshow(inp, title=None): inp = inp.numpy().transpose((1, 2, 0)) mean = np.array([0.485, 0.456, 0.406]) std = np.array([0.229, 0.224, 0.225]) inp = std * inp + mean inp = np.clip(inp, 0, 1) plt.imshow(inp) if title is not None: plt.title(title) plt.pause(0.001) # 모델 학습 및 검증 함수 def train_model(model, dataloaders, criterion, optimizer, scheduler, num_epochs=25): since = time.time() best_model_wts = copy.deepcopy(model.state_dict()) best_acc = 0.0 for epoch in range(num_epochs): print(f'Epoch {epoch}/{num_epochs-1}') print('-' * 10) 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 = inputs.to(device) labels = 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) if phase == 'train': scheduler.step() epoch_loss = running_loss / dataset_sizes[phase] epoch_acc = running_corrects.double() / dataset_sizes[phase] print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}') writer.add_scalars('Loss', {phase: epoch_loss}, epoch) writer.add_scalars('Accuracy', {phase: epoch_acc}, epoch) if phase == 'val' and epoch_acc > best_acc: best_acc = epoch_acc best_model_wts = copy.deepcopy(model.state_dict()) print() 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 if __name__ == "__main__": print(f"Training dataset size: {len(image_datasets['train'])}") print(f"Validation dataset size: {len(image_datasets['val'])}") model_ft = train_model(model_ft, dataloaders, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=num_epochs) torch.save(model_ft.state_dict(), os.path.join(log_dir, "model.pth")) writer.close() | cs |
10) 필기체 숫자 0~9를 분류하시오.
- 필기체 숫자 편집기를 제작하여 숫자당 100장의 훈련데이터를 제작
- 2가지 이상의 모델(Resnet, ViT)을 이용하여 훈련 수행
- 정확도 비교
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, models, transforms from torch.utils.data import DataLoader, Dataset from torch.utils.tensorboard import SummaryWriter from tqdm import tqdm import os from datetime import datetime import numpy as np from PIL import Image import time import copy import matplotlib.pyplot as plt import timm import datetime # 하이퍼파라미터 설정 data_dir = r"C:\Users\Admin\LEE_workspace\pytorch\dataset2" # 데이터셋 디렉토리 train_dir = os.path.join(data_dir, "train") # 학습 데이터 디렉토리 val_dir = os.path.join(data_dir, "val") # 검증 데이터 디렉토리 batch_size = 32 # 배치 크기 num_epochs = 50 # 에포크 수 learning_rate = 0.001 # 학습률 feature_extract = True # 특징 추출 여부 num_classes = 10 # 클래스 수 # 데이터 전처리 설정 data_transforms = { 'train': transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomApply([transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3, hue=0.1)], p=0.5), # 랜덤 컬러 지터 transforms.ToTensor(), # 텐서로 변환 transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 정규화 ]), 'val': transforms.Compose([ transforms.Resize((224, 224)), # 리사이즈 transforms.ToTensor(), # 텐서로 변환 transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 정규화 ]), } # 사용자 정의 데이터셋 class HandwrittenDigitsDataset(Dataset): def __init__(self, data_dir, transform=None): self.data_dir = data_dir self.transform = transform self.images = [] self.labels = [] for label in sorted(os.listdir(data_dir)): for img_file in os.listdir(os.path.join(data_dir, label)): self.images.append(os.path.join(data_dir, label, img_file)) self.labels.append(int(label)) def __len__(self): return len(self.images) def __getitem__(self, idx): img_path = self.images[idx] image = Image.open(img_path).convert("RGB") # 이미지를 RGB로 변환 label = self.labels[idx] if self.transform: image = self.transform(image) # 변환 적용 return image, label # 데이터셋 로드 train_dataset = HandwrittenDigitsDataset(train_dir, transform=data_transforms['train']) # 학습 데이터셋 val_dataset = HandwrittenDigitsDataset(val_dir, transform=data_transforms['val']) # 검증 데이터셋 # 데이터로더 설정 train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4) # 학습 데이터로더 val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=4) # 검증 데이터로더 dataloaders = {'train': train_loader, 'val': val_loader} dataset_sizes = {'train': len(train_dataset), 'val': len(val_dataset)} # 장치 설정 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # GPU 사용 여부 설정 # 모델 초기화 함수 def initialize_model(model_name, num_classes, feature_extract, use_pretrained=True): if model_name == "resnet101": model = models.resnet101(weights=models.ResNet101_Weights.IMAGENET1K_V1 if use_pretrained else None) for param in model.parameters(): param.requires_grad = False # 모든 파라미터 고정 model.fc.weight.requires_grad = True # 최종 레이어만 학습 model.fc.bias.requires_grad = True num_ftrs = model.fc.in_features model.fc = nn.Linear(num_ftrs, num_classes) # 최종 레이어 수정 # elif model_name == "vit": # model = timm.create_model('vit_base_patch16_224', pretrained=use_pretrained, num_classes=num_classes) # for param in model.parameters(): # param.requires_grad = False # 모든 파라미터 고정 # model.head.weight.requires_grad = True # 최종 레이어만 학습 # model.head.bias.requires_grad = True # num_ftrs = model.head.in_features # model.head = nn.Linear(num_ftrs, num_classes) # 최종 레이어 수정 return model # 모델 학습 및 검증 함수 def train_model(model, dataloaders, criterion, optimizer, scheduler, num_epochs=25): since = time.time() best_model_wts = copy.deepcopy(model.state_dict()) best_acc = 0.0 train_losses, val_losses = [], [] train_accs, val_accs = [], [] plt.ion() # 인터랙티브 모드 켜기 plt.figure(figsize=(10, 5)) plt.subplot(1, 2, 1) plt.title('Loss') #plt.legend('Train Loss','Val Loss') plt.subplot(1, 2, 2) plt.title('Accuracy') #plt.legend('Train Acc','Val Acc') for epoch in range(num_epochs): print(f'Epoch {epoch}/{num_epochs-1}') print('-' * 10) for phase in ['train', 'val']: if phase == 'train': model.train() # 학습 모드 else: model.eval() # 평가 모드 running_loss = 0.0 running_corrects = 0 top5_corrects = 0 for inputs, labels in dataloaders[phase]: inputs = inputs.to(device) labels = 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) top5_corrects += torch.sum(torch.topk(outputs, 5, dim=1)[1] == labels.view(-1, 1)).item() # acc-5 정확도 계산 if phase == 'train': scheduler.step() epoch_loss = running_loss / dataset_sizes[phase] epoch_acc = running_corrects.double() / dataset_sizes[phase] epoch_top5_acc = top5_corrects / dataset_sizes[phase] print(f'{phase} Loss: {epoch_loss:.4f} Acc1: {epoch_acc:.4f} Acc5: {epoch_top5_acc:.4f}') if phase == 'train': train_losses.append(epoch_loss) train_accs.append(epoch_acc.cpu().numpy()) # GPU에서 CPU로 이동 후 NumPy 배열로 변환 else: val_losses.append(epoch_loss) val_accs.append(epoch_acc.cpu().numpy()) # GPU에서 CPU로 이동 후 NumPy 배열로 변환 if phase == 'val' and epoch_acc > best_acc: best_acc = epoch_acc best_model_wts = copy.deepcopy(model.state_dict()) # 학습 과정 시각화 plt.subplot(1, 2, 1) line1,=plt.plot(train_losses, label='Train Loss') line2,=plt.plot(val_losses, label='Val Loss') #plt.legend([train_losses,val_losses],['Train Loss','Val Loss']) plt.legend(handles=[line1,line2]) plt.subplot(1, 2, 2) line3,=plt.plot(train_accs, label='Train Acc') line4,=plt.plot(val_accs, label='Val Acc') #plt.legend([train_accs,val_accs],['Train Acc','Val Acc']) plt.legend(handles=[line3,line4]) plt.draw() plt.pause(0.001) # 잠깐 멈춤으로 그래프 갱신 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 # 증식된 데이터 시각화 함수 def visualize_augmentation(data_loader, num_images=6): images_so_far = 0 fig = plt.figure(figsize=(15, 15)) for i, (inputs, labels) in enumerate(data_loader): inputs = inputs.numpy().transpose((0, 2, 3, 1)) inputs = np.clip((inputs * np.array([0.229, 0.224, 0.225]) + np.array([0.485, 0.456, 0.406])), 0, 1) for j in range(inputs.shape[0]): images_so_far += 1 ax = plt.subplot(num_images // 2, 2, images_so_far) ax.axis('off') ax.set_title(f'Label: {labels[j].item()}') ax.imshow(inputs[j]) if images_so_far == num_images: plt.show() return if __name__ == '__main__': # 증식된 데이터 시각화 print("Visualizing Augmented Data") visualize_augmentation(train_loader) # 모델 초기화 resnet_model = initialize_model("resnet101", num_classes, feature_extract, use_pretrained=True) #vit_model = initialize_model("vit", num_classes, feature_extract, use_pretrained=True) # 손실 함수 설정 criterion = nn.CrossEntropyLoss() # 옵티마이저 설정 resnet_optimizer = optim.AdamW(filter(lambda p: p.requires_grad, resnet_model.parameters()), lr=learning_rate) #vit_optimizer = optim.AdamW(filter(lambda p: p.requires_grad, vit_model.parameters()), lr=learning_rate) # 학습률 스케줄러 설정 resnet_scheduler = optim.lr_scheduler.StepLR(resnet_optimizer, step_size=7, gamma=0.1) #vit_scheduler = optim.lr_scheduler.StepLR(vit_optimizer, step_size=7, gamma=0.1) # 모델을 GPU로 이동 resnet_model = resnet_model.to(device) # vit_model = vit_model.to(device) # 모델 학습 print("Training ResNet Model") resnet_model = train_model(resnet_model, dataloaders, criterion, resnet_optimizer, resnet_scheduler, num_epochs=num_epochs) #print("Training ViT Model") # vit_model = train_model(vit_model, dataloaders, criterion, vit_optimizer, vit_scheduler, num_epochs=num_epochs) # 모델 저장 torch.save(resnet_model.state_dict(), os.path.join("resnet_model.pth")) # torch.save(vit_model.state_dict(), os.path.join("vit_model.pth")) # 모델 구조와 가중치 모두를 저장 torch.save(resnet_model, os.path.join("resnet_model_full.pth")) #torch.save(vit_model, os.path.join("vit_model_full.pth")) plt.ioff() # 인터랙티브 모드 끄기 plt.show() # 마지막 그래프 유지 plt.savefig('그래프.png') | cs |
첫댓글 텐서보드사용시 실행하는 명령어도 설명해주고 홈페이지에서 접속하는 방법도 추가할것