-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/BowonKwon/AISA5
- Loading branch information
Showing
5 changed files
with
538 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# 패키지 임포트 | ||
import os # 경로 설정을 위한 os 패키지 임포트 | ||
import torch # 파이토치 패키지 임포트 | ||
import torch.nn as nn # 파이토치의 nn 패키지 임포트 | ||
from PIL import Image # 이미지를 다루기 위한 PIL 패키지 임포트 | ||
from torchvision.transforms import Resize # 이미지 크기를 조절하는 함수 임포트 | ||
from torchvision.transforms import ToTensor # 이미지를 텐서로 변환하는 함수 임포트 | ||
|
||
# 타겟하는 학습 세팅을 설정 | ||
target_folder = '../../주중수업/5주차/results/1' # 타겟 폴더 설정 | ||
assert os.path.exists(target_folder), 'target folder does not exists' # 타겟 폴더가 존재하는지 확인 | ||
|
||
# 하이퍼파라미터 로드 | ||
with open(os.path.join(target_folder, 'hparam.txt'), 'r') as f: # hparam.txt 파일을 읽기 모드로 열기 | ||
data = f.readlines() # 파일의 모든 줄을 읽어서 리스트로 저장 | ||
print(data) # 읽어온 데이터 출력 | ||
|
||
lr = float(data[0].strip()) # 학습률 저장 | ||
image_size = int(data[1].strip()) # 이미지 사이즈 저장 | ||
num_classes = int(data[2].strip()) # 클래스 개수 저장 | ||
batch_size = int(data[3].strip()) # 배치 크기 저장 | ||
hidden_size = int(data[4].strip()) # 은닉층 크기 저장 | ||
epochs = int(data[5].strip()) # 에포크 수 저장 | ||
results_folder = data[6].strip() # 결과 폴더 저장 | ||
|
||
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # GPU 사용 여부에 따라 device 설정 | ||
|
||
# 모델 class 만들기 | ||
class MLP(nn.Module): # nn.Module을 상속받는 MLP 클래스 선언 | ||
def __init__(self, image_size, hidden_size, num_classes): # 클래스 초기화: MLP 레이어 정의 | ||
super().__init__() # 상속받은 상위 클래스의 초기화 메서드 호출 | ||
self.image_size = image_size # 이미지 사이즈 저장 | ||
self.mlp1 = nn.Linear(image_size * image_size, hidden_size) # 첫 번째 MLP 레이어 선언(입력층 -> 은닉층1) | ||
self.mlp2 = nn.Linear(hidden_size, hidden_size) # 두 번째 MLP 레이어 선언(은닉층1 -> 은닉층2) | ||
self.mlp3 = nn.Linear(hidden_size, hidden_size) # 세 번째 MLP 레이어 선언(은닉층2 -> 은닉층3) | ||
self.mlp4 = nn.Linear(hidden_size, num_classes) # 네 번째 MLP 레이어 선언(은닉층3 -> 출력층) | ||
def forward(self, x): # 순전파: 데이터가 레이어 통과하는 방식 지정 | ||
batch_size = x.shape[0] # 입력 텐서의 배치 크기 저장(x: [batch_size, 28, 28, 1]) | ||
x = torch.reshape(x, (-1, image_size * image_size)) # 28*28 픽셀 이미지를 1차원 벡터로 변환(펼치기) | ||
# 순전파 수행: 입력 이미지를 순차적으로 MLP 레이어에 통과시킴 | ||
x = self.mlp1(x) # [batch_size, 500] | ||
x = self.mlp2(x) # [batch_size, 500] | ||
x = self.mlp3(x) # [batch_size, 500] | ||
x = self.mlp4(x) # [batch_size, 10] | ||
# 최종 출력 반환 | ||
return x | ||
|
||
# 모델 선언 | ||
myMLP = MLP(image_size, hidden_size, num_classes).to(device) | ||
|
||
# 저장된 모델 가중치 불러오기 | ||
ckpt = torch.load( # 저장된 모델 가중치 불러오기 | ||
os.path.join( # 경로 설정 | ||
target_folder, 'myMLP_best.ckpt' # 타겟 폴더 내의 myMLP_best.ckpt 파일 경로 | ||
) | ||
) | ||
myMLP.load_state_dict(ckpt) # 모델에 가중치 저장 | ||
|
||
# 추론 데이터를 가지고 오기 | ||
image_path = './test_image.jpg' # 추론할 이미지 경로 | ||
assert os.path.exists(image_path), 'target image does not exists' # 이미지가 존재하는지 확인 | ||
input_image = Image.open().convert('L') # 이미지를 흑백으로 변환 | ||
|
||
# 학습 과정에서 사용했던 전처리 과정을 그대로 실행 | ||
resizer = Resize(image_size) # 크기 맞추기: 이미지 크기를 조절하는 함수 선언 | ||
totensor = ToTensor() # 크기 맞추기: 이미지를 텐서로 변환하는 함수 선언 | ||
image = totensor(resizer(input_image)).to(device) # 이미지를 텐서로 변환 후 device로 이동 | ||
|
||
# 모델 추론 진행 | ||
output = myMLP(image) # 모델에 이미지 입력 후 출력값 저장 | ||
# 추론 결과를 우리가 이해할 수 있는 형태로 변환 | ||
output = torch.argmax(output).item() # 출력값 중 가장 큰 값의 인덱스를 추론 결과로 저장 | ||
|
||
print(f'Model says, the image is {output}') # 모델이 추론한 결과 출력 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# 패키지 임포트 | ||
# 경로 설정을 위한 os 패키지 임포트 | ||
# 파이토치 패키지 임포트 | ||
# 파이토치의 nn 패키지 임포트 | ||
# 이미지를 다루기 위한 PIL 패키지 임포트 | ||
# 이미지 크기를 조절하는 함수 임포트 | ||
# 이미지를 텐서로 변환하는 함수 임포트 | ||
|
||
# 타겟하는 학습 세팅을 설정 | ||
# 타겟 폴더 설정 | ||
# 타겟 폴더가 존재하는지 확인 | ||
|
||
# 하이퍼파라미터 로드 | ||
# hparam.txt 파일을 읽기 모드로 열기 | ||
# 파일의 모든 줄을 읽어서 리스트로 저장 | ||
# 읽어온 데이터 출력 | ||
|
||
# 학습률 저장 | ||
# 이미지 사이즈 저장 | ||
# 클래스 개수 저장 | ||
# 배치 크기 저장 | ||
# 은닉층 크기 저장 | ||
# 에포크 수 저장 | ||
# 결과 폴더 저장 | ||
|
||
# GPU 사용 여부에 따라 device 설정 | ||
|
||
# 모델 class 만들기 | ||
# nn.Module을 상속받는 MLP 클래스 선언 | ||
# 클래스 초기화: MLP 레이어 정의 | ||
# 상속받은 상위 클래스의 초기화 메서드 호출 | ||
# 이미지 사이즈 저장 | ||
# 첫 번째 MLP 레이어 선언(입력층 -> 은닉층1) | ||
# 두 번째 MLP 레이어 선언(은닉층1 -> 은닉층2) | ||
# 세 번째 MLP 레이어 선언(은닉층2 -> 은닉층3) | ||
# 네 번째 MLP 레이어 선언(은닉층3 -> 출력층) | ||
# 순전파: 데이터가 레이어 통과하는 방식 지정 | ||
# 입력 텐서의 배치 크기 저장(x: [batch_size, 28, 28, 1]) | ||
# 28*28 픽셀 이미지를 1차원 벡터로 변환(펼치기) | ||
# 순전파 수행: 입력 이미지를 순차적으로 MLP 레이어에 통과시킴 | ||
# [batch_size, 500] | ||
# [batch_size, 500] | ||
# [batch_size, 500] | ||
# [batch_size, 10] | ||
# 최종 출력 반환 | ||
|
||
# 모델 선언 | ||
|
||
# 저장된 모델 가중치 불러오기 | ||
# 저장된 모델 가중치 불러오기 | ||
# 경로 설정 | ||
# 타겟 폴더 내의 myMLP_best.ckpt 파일 경로 | ||
# 모델에 가중치 저장 | ||
|
||
# 추론 데이터를 가지고 오기 | ||
# 추론할 이미지 경로 | ||
# 이미지가 존재하는지 확인 | ||
# 이미지를 흑백으로 변환 | ||
|
||
# 학습 과정에서 사용했던 전처리 과정을 그대로 실행 | ||
# 크기 맞추기: 이미지 크기를 조절하는 함수 선언 | ||
# 크기 맞추기: 이미지를 텐서로 변환하는 함수 선언 | ||
# 이미지를 텐서로 변환 후 device로 이동 | ||
|
||
# 모델 추론 진행 | ||
# 모델에 이미지 입력 후 출력값 저장 | ||
# 추론 결과를 우리가 이해할 수 있는 형태로 변환 | ||
# 출력값 중 가장 큰 값의 인덱스를 추론 결과로 저장 | ||
|
||
# 모델이 추론한 결과 출력 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
# 패키지 임포트 | ||
import os # os 패키지 임포트 | ||
import torch # 파이토치 패키지 임포트 | ||
import torch.nn as nn # nn 패키지 임포트 | ||
from torchvision.datasets import MNIST # MNIST 데이터셋 불러오기 | ||
from torchvision.transforms import ToTensor # ToTensor 클래스 임포트 | ||
from torch.utils.data import DataLoader # DataLoader 클래스 임포트 | ||
from torch.optim import Adam # Adam 클래스 임포트 | ||
|
||
# hyperparameter 선언(학습률, 이미지 사이즈, 클래스 개수, 배치 사이즈, 은닉층 사이즈, 에포크 수, 결과 저장 폴더) | ||
lr = 0.001 | ||
image_size = 28 | ||
num_classes = 10 | ||
batch_size = 100 | ||
hidden_size = 500 | ||
epochs = 3 | ||
results_folder = 'results' | ||
|
||
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 디바이스 설정 | ||
|
||
# 저장 | ||
# 상위 저장 폴더가 없으면 상위 저장 폴더 생성 | ||
if not os.path.exists(results_folder): | ||
os.makedirs(results_folder) | ||
# 결과 저장할 하위 타깃 폴더 생성 | ||
target_folder_name = max([0] + [int(e) for e in os.listdir(results_folder)])+1 # 하위 타깃 폴더 이름 | ||
target_folder = os.path.join(results_folder, str(target_folder_name)) # 하위 타깃 폴더 경로 | ||
os.makedirs(target_folder) # 하위 타깃 폴더 생성 | ||
# 타깃 폴더에 하이퍼파라미터 저장 | ||
with open(os.path.join(target_folder, 'hparam.txt'), 'w') as f: # 타깃 폴더에 hparam.txt 파일 생성 | ||
f.write(f'{lr}\n') | ||
f.write(f'{image_size}\n') | ||
f.write(f'{num_classes}\n') | ||
f.write(f'{batch_size}\n') | ||
f.write(f'{hidden_size}\n') | ||
f.write(f'{epochs}\n') | ||
f.write(f'{results_folder}\n') | ||
|
||
# 모델 설계도 그리기 | ||
class MLP(nn.Module): # nn.Module을 상속받는 MLP 클래스 선언 | ||
def __init__(self, image_size, hidden_size, num_classes): # 클래스 초기화: MLP 레이어 정의 | ||
super().__init__() # 상속받은 상위 클래스의 초기화 메서드 호출 | ||
self.image_size = image_size # 이미지 사이즈 저장 | ||
self.mlp1 = nn.Linear(image_size * image_size, hidden_size) # 첫 번째 MLP 레이어 선언(입력층 -> 은닉층1) | ||
self.mlp2 = nn.Linear(hidden_size, hidden_size) # 두 번째 MLP 레이어 선언(은닉층1 -> 은닉층2) | ||
self.mlp3 = nn.Linear(hidden_size, hidden_size) # 세 번째 MLP 레이어 선언(은닉층2 -> 은닉층3) | ||
self.mlp4 = nn.Linear(hidden_size, num_classes) # 네 번째 MLP 레이어 선언(은닉층3 -> 출력층) | ||
def forward(self, x): # 순전파: 데이터가 레이어 통과하는 방식 지정 | ||
batch_size = x.shape[0] # 입력 텐서의 배치 크기 저장(x: [batch_size, 28, 28, 1]) | ||
x = torch.reshape(x, (-1, image_size * image_size)) # 28*28 픽셀 이미지를 1차원 벡터로 변환(펼치기) | ||
# 순전파 수행: 입력 이미지를 순차적으로 MLP 레이어에 통과시킴 | ||
x = self.mlp1(x) # [batch_size, 500] | ||
x = self.mlp2(x) # [batch_size, 500] | ||
x = self.mlp3(x) # [batch_size, 500] | ||
x = self.mlp4(x) # [batch_size, 10] | ||
# 최종 출력 반환 | ||
return x | ||
|
||
# 설계도를 바탕으로 모델 만들기 <- hyperparmeter 사용 | ||
myMLP = MLP(image_size, hidden_size, num_classes).to(device) | ||
|
||
# 데이터 불러오기 | ||
# dataset 설정(학습, 테스트 데이터) | ||
train_mnist = MNIST(root='../../data/mnist', train=True, transform=ToTensor(), download=True) | ||
test_mnist = MNIST(root='../../data/mnist', train=False, transform=ToTensor(), download=True) | ||
# dataloader 설정(학습, 테스트 데이터) | ||
train_loader = DataLoader(dataset=train_mnist, batch_size=batch_size, shuffle=True) | ||
test_loader = DataLoader(dataset=test_mnist, batch_size=batch_size, shuffle=False) | ||
|
||
# Loss 선언 | ||
loss_fn = nn.CrossEntropyLoss() | ||
# Optimizer 선언 | ||
optim = Adam(params=myMLP.parameters(), lr=lr) | ||
|
||
# 평가함수 구현 | ||
# 전체 데이터에 대한 정확도를 계산하는 함수 | ||
def evaluate(model, loader, device): # 모델, 데이터 로더, 디바이스를 인자로 받음 | ||
with torch.no_grad(): # 그래디언트 계산 비활성화 | ||
model.eval() # 모델을 평가 모드로 설정 | ||
total = 0 # 전체 데이터 개수 저장 변수 | ||
correct = 0 # 정답 개수 저장 변수 | ||
for images, targets in loader: # 데이터 로더로부터 미니배치를 하나씩 꺼내옴 | ||
images, targets = images.to(device), targets.to(device) # 디바이스에 데이터를 보냄 | ||
output = model(images) # 모델에 미니배치 데이터 입력하여 결괏값 계산 | ||
output_index = torch.argmax(output, dim = 1) # 결괏값 중 가장 큰 값의 인덱스를 뽑아냄 | ||
total += targets.shape[0] # 전체 데이터 개수 누적 | ||
correct += (output_index == targets).sum().item() # 정답 개수 누적 | ||
|
||
acc = correct / total * 100 # 정확도(%) 계산 | ||
model.train() # 모델을 학습 모드로 설정 | ||
return acc # 정확도(%) 반환 | ||
# 클래스별 정확도를 계산하는 함수 | ||
def evaluate_by_class(model, loader, device, num_classes): # 모델, 데이터 로더, 디바이스, 클래스 개수를 인자로 받음 | ||
with torch.no_grad(): # 그래디언트 계산 비활성화 | ||
model.eval() # 모델을 평가 모드로 설정 | ||
total = torch.zeros(num_classes) # 클래스별 전체 데이터 개수 저장 변수 | ||
correct = torch.zeros(num_classes) # 클래스별 정답 개수 저장 변수 | ||
for images, targets in loader: # 데이터 로더로부터 미니배치를 하나씩 꺼내옴 | ||
images, targets = images.to(device), targets.to(device) # 디바이스에 데이터를 보냄 | ||
output = model(images) # 모델에 미니배치 데이터 입력하여 결괏값 계산 | ||
output_index = torch.argmax(output, dim = 1) # 결괏값 중 가장 큰 값의 인덱스를 뽑아냄 | ||
for _class in range(num_classes): # 클래스 개수만큼 반복 | ||
total[_class] += (targets == _class).sum().item() # 클래스별 전체 데이터 개수 누적 | ||
correct[_class] += ((targets == _class) * (output_index == _class)).sum().item() # 클래스별 정답 개수 누적 | ||
|
||
acc = correct / total * 100 # 클래스별 정확도(%) 계산 | ||
model.train() # 모델을 학습 모드로 설정 | ||
return acc # 클래스별 정확도(%) 반환 | ||
|
||
_max = -1 # 최대 정확도 저장 변수 | ||
# 학습을 위한 반복 (Loop) for / while | ||
for epoch in range(epochs): | ||
# 입력할 데이터를 위해 데이터 준비 (dataloader) | ||
for idx, (images, targets) in enumerate(train_loader): | ||
# 데이터와 타깃을 디바이스에 올리기 | ||
images = images.to(device) | ||
targets = targets.to(device) | ||
# 모델에 데이터를 넣기 | ||
output = myMLP(images) | ||
# 모델의 출력과 정답을 비교하기 (Loss 사용) | ||
loss = loss_fn(output, targets) | ||
# 역전파 수행 | ||
loss.backward() | ||
# 가중치 업데이트 | ||
optim.step() | ||
# 그래디언트 초기화 | ||
optim.zero_grad() | ||
|
||
# 100번 반복마다 loss 출력 | ||
if idx % 100 == 0: | ||
print(loss) | ||
|
||
acc = evaluate(myMLP, test_loader, device) # 전체 데이터에 대한 정확도 계산 | ||
# acc = evaluate_by_class(myMLP, test_loader, device, num_classes) # 클래스별 정확도 계산 | ||
|
||
# 정확도가 높아지면 모델 저장 | ||
if _max < acc : # acc가 높아지면 | ||
print('새로운 acc 등장, 모델 weight 업데이트', acc) # acc 출력 | ||
_max = acc # _max에 acc 저장 | ||
# 모델 저장 | ||
torch.save( | ||
myMLP.state_dict(), | ||
os.path.join(target_folder, 'myMLP_best.ckpt') | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# 패키지 임포트 | ||
# os 패키지 임포트 | ||
# 파이토치 패키지 임포트 | ||
# nn 패키지 임포트 | ||
# MNIST 데이터셋 불러오기 | ||
# ToTensor 클래스 임포트 | ||
# DataLoader 클래스 임포트 | ||
# Adam 클래스 임포트 | ||
|
||
# hyperparameter 선언(학습률, 이미지 사이즈, 클래스 개수, 배치 사이즈, 은닉층 사이즈, 에포크 수, 결과 저장 폴더) | ||
|
||
# 디바이스 설정 | ||
|
||
# 상위 저장 폴더가 없으면 상위 저장 폴더 생성 | ||
# 결과 저장할 하위 타깃 폴더 생성 | ||
# 하위 타깃 폴더 이름 | ||
# 하위 타깃 폴더 경로 | ||
# 하위 타깃 폴더 생성 | ||
# 타깃 폴더에 하이퍼파라미터 저장 | ||
# 타깃 폴더에 hparam.txt 파일 생성 | ||
|
||
# 모델 설계도 그리기 | ||
# nn.Module을 상속받는 MLP 클래스 선언 | ||
# 클래스 초기화: MLP 레이어 정의 | ||
# 상속받은 상위 클래스의 초기화 메서드 호출 | ||
# 이미지 사이즈 저장 | ||
# 첫 번째 MLP 레이어 선언(입력층 -> 은닉층1) | ||
# 두 번째 MLP 레이어 선언(은닉층1 -> 은닉층2) | ||
# 세 번째 MLP 레이어 선언(은닉층2 -> 은닉층3) | ||
# 네 번째 MLP 레이어 선언(은닉층3 -> 출력층) | ||
# 순전파: 데이터가 레이어 통과하는 방식 지정 | ||
# 입력 텐서의 배치 크기 저장(x: [batch_size, 28, 28, 1]) | ||
# 28*28 픽셀 이미지를 1차원 벡터로 변환(펼치기) | ||
# 순전파 수행: 입력 이미지를 순차적으로 MLP 레이어에 통과시킴 | ||
# [batch_size, 500] | ||
# [batch_size, 500] | ||
# [batch_size, 500] | ||
# [batch_size, 10] | ||
# 최종 출력 반환 | ||
|
||
# 설계도를 바탕으로 모델 만들기 <- hyperparmeter 사용 | ||
|
||
# 데이터 불러오기 | ||
# dataset 설정(학습, 테스트 데이터) | ||
# dataloader 설정(학습, 테스트 데이터) | ||
|
||
# Loss 선언 | ||
# Optimizer 선언 | ||
|
||
# 평가함수 구현 | ||
# 전체 데이터에 대한 정확도를 계산하는 함수 | ||
# 모델, 데이터 로더, 디바이스를 인자로 받음 | ||
# 그래디언트 계산 비활성화 | ||
# 모델을 평가 모드로 설정 | ||
# 전체 데이터 개수 저장 변수 | ||
# 정답 개수 저장 변수 | ||
# 데이터 로더로부터 미니배치를 하나씩 꺼내옴 | ||
# 디바이스에 데이터를 보냄 | ||
# 모델에 미니배치 데이터 입력하여 결괏값 계산 | ||
# 결괏값 중 가장 큰 값의 인덱스를 뽑아냄 | ||
# 전체 데이터 개수 누적 | ||
# 정답 개수 누적 | ||
# 정확도(%) 계산 | ||
# 모델을 학습 모드로 설정 | ||
# 정확도(%) 반환 | ||
|
||
# 클래스별 정확도를 계산하는 함수 | ||
# 모델, 데이터 로더, 디바이스, 클래스 개수를 인자로 받음 | ||
# 그래디언트 계산 비활성화 | ||
# 모델을 평가 모드로 설정 | ||
# 클래스별 정답 개수 저장 변수 | ||
# 클래스별 전체 데이터 개수 저장 변수 | ||
# 데이터 로더로부터 미니배치를 하나씩 꺼내옴 | ||
# 디바이스에 데이터를 보냄 | ||
# 모델에 미니배치 데이터 입력하여 결괏값 계산 | ||
# 결괏값 중 가장 큰 값의 인덱스를 뽑아냄 | ||
# 클래스 개수만큼 반복 | ||
# 클래스별 전체 데이터 개수 누적 | ||
# 클래스별 정답 개수 누적 | ||
# 클래스별 정확도(%) 계산 | ||
# 모델을 학습 모드로 설정 | ||
# 클래스별 정확도(%) 반환 | ||
|
||
# 최대 정확도 저장 변수 | ||
|
||
# 학습을 위한 반복 (Loop) for / while | ||
# 입력할 데이터를 위해 데이터 준비 (dataloader) | ||
# 데이터와 타깃을 디바이스에 올리기 | ||
# 모델에 데이터를 넣기 | ||
# 모델의 출력과 정답을 비교하기 (Loss 사용) | ||
# 역전파 수행 | ||
# 가중치 업데이트 | ||
# 그래디언트 초기화 | ||
|
||
# 100번 반복마다 loss 출력 | ||
|
||
# 전체 데이터에 대한 정확도 계산 | ||
# 클래스별 정확도 계산 | ||
|
||
# 정확도가 높아지면 모델 저장 | ||
# acc가 높아지면 | ||
# acc 출력 | ||
# _max에 acc 저장 | ||
# 모델 저장 |
Oops, something went wrong.