Skip to content

Commit

Permalink
[Add] 5주차 주말 학습파일 업로드
Browse files Browse the repository at this point in the history
  • Loading branch information
BowonKwon committed Aug 6, 2023
1 parent bf67c2f commit ece35d1
Show file tree
Hide file tree
Showing 2 changed files with 303 additions and 0 deletions.
157 changes: 157 additions & 0 deletions 주말수업/5주차/1_train_follow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# 패키지 임포트
import os # os 패키지 임포트
import json # json 패키지 임포트
import torch # 파이토치 패키지 임포트
import argparse # argparse 패키지 임포트
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 클래스 임포트

def parse_args(): # 하이퍼파라미터 선언 함수
parser = argparse.ArgumentParser() # parser 객체 생성
# 하이퍼파라미터 선언
parser.add_argument('--lr', type=float, default=0.001)
parser.add_argument('--image_size', type=int, default=28)
parser.add_argument('--num_classes', type=int, default=10)
parser.add_argument('--batch_size', type=int, default=100)
parser.add_argument('--hidden_size', type=int, default=1000)
parser.add_argument('--total_epochs', type=int, default=3)
parser.add_argument('--results_folder', type=str, default='results')
parser.add_argument('--device', default=torch.device('cuda' if torch.cuda.is_available() else 'cpu'))
parser.add_argument('--do_save', action='store_true', help='if given, save results') # 예시1
parser.add_argument('--data', nargs='+', type=str) # 예시2


args = parser.parse_args() # 파싱한 하이퍼파라미터 저장
return args # 하이퍼파라미터 반환

def main(): # 메인 함수
args = parse_args() # 하이퍼파라미터 호출

# 저장
# 상위 저장 폴더가 없으면 상위 저장 폴더 생성
if not os.path.exists(args.results_folder):
os.makedirs(args.results_folder)
# 결과 저장할 하위 타깃 폴더 생성
target_folder_name = max([0] + [int(e) for e in os.listdir(args.results_folder)])+1 # 하위 타깃 폴더 이름
target_folder = os.path.join(args.results_folder, str(target_folder_name)) # 하위 타깃 폴더 경로
os.makedirs(target_folder) # 하위 타깃 폴더 생성
# 타깃 폴더에 하이퍼파라미터 저장
with open(os.path.join(target_folder, 'hparam.json'), 'w') as f: # 타깃 폴더에 hparam.json 파일 생성
write_args = args.__dict__ # 하이퍼파라미터 딕셔너리 저장
del write_args['device'] # 딕셔너리에서 device 키 삭제
json.dump(args.__dict__, f, indent=4) # 딕셔너리를 json 파일로 저장

assert() # assert: 조건이 참이면 아무런 일도 일어나지 않지만, 조건이 거짓이면 AssertionError 발생

# 모델 설계도 그리기
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, self.image_size * self.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(args.image_size, args.hidden_size, args.num_classes).to(args.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=args.batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_mnist, batch_size=args.batch_size, shuffle=False)

# Loss 선언
loss_fn = nn.CrossEntropyLoss()
# Optimizer 선언
optim = Adam(params=myMLP.parameters(), lr=args.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(args.total_epochs):
# 입력할 데이터를 위해 데이터 준비 (dataloader)
for idx, (images, targets) in enumerate(train_loader):
# 데이터와 타깃을 디바이스에 올리기
images = images.to(args.device)
targets = targets.to(args.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, args.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')
)
# 이 파일이 메인 파일이면 main 함수 실행
if __name__ == '__main__' :
main()
146 changes: 146 additions & 0 deletions 주말수업/5주차/2_train_comment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# 패키지 임포트
# os 패키지 임포트
# json 패키지 임포트
# 파이토치 패키지 임포트
# argparse 패키지 임포트
# nn 패키지 임포트
# MNIST 데이터셋 불러오기
# ToTensor 클래스 임포트
# DataLoader 클래스 임포트
# Adam 클래스 임포트

# 하이퍼파라미터 선언 함수
# parser 객체 생성
# 하이퍼파라미터 선언(학습률, 이미지 사이즈, 클래스 개수, 배치 크기, 은닉층 크기, 에포크 수, 결과 폴더)
# 예시1: --do_save
# 예시2: --data

# 파싱한 하이퍼파라미터 저장
# 하이퍼파라미터 반환

# 메인 함수
# 하이퍼파라미터 호출

# 저장
# 상위 저장 폴더가 없으면 상위 저장 폴더 생성

# 결과 저장할 하위 타깃 폴더 생성
# 하위 타깃 폴더 이름
# 하위 타깃 폴더 경로
# 하위 타깃 폴더 생성
# 타깃 폴더에 하이퍼파라미터 저장
# 타깃 폴더에 hparam.json 파일 생성
# 하이퍼파라미터 딕셔너리 저장args를 딕셔너리로 변환
# 딕셔너리에서 device 키 삭제device 항목 삭제
# 딕셔너리를 json 파일로 저장args를 json 형식으로 저장

# assert: 조건이 참이면 아무런 일도 일어나지 않지만, 조건이 거짓이면 AssertionError 발생

# 모델 설계도 그리기
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, self.image_size * self.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(args.image_size, args.hidden_size, args.num_classes).to(args.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=args.batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_mnist, batch_size=args.batch_size, shuffle=False)

# Loss 선언
loss_fn = nn.CrossEntropyLoss()
# Optimizer 선언
optim = Adam(params=myMLP.parameters(), lr=args.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(args.total_epochs):
# 입력할 데이터를 위해 데이터 준비 (dataloader)
for idx, (images, targets) in enumerate(train_loader):
# 데이터와 타깃을 디바이스에 올리기
images = images.to(args.device)
targets = targets.to(args.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, args.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')
)
# 이 파일이 메인 파일이면 main 함수 실행

0 comments on commit ece35d1

Please sign in to comment.