AI 활용 소프트웨어 개발/AI, 머신러닝, 딥러닝

pytorch 딥러닝, CNN 학습 흐름 정리

ha2yong 2025. 7. 30. 23:15

흐름 요약

1.데이터 전처리

- 데이터 EDA, 필요없는 데이터 제거, 라벨인코딩, 원핫 인코딩 등 데이터 가공 

- 데이터를 알맞게 변환할 transform 객체 생성

- 데이터를 Train, Validation, Test 용으로 분류 + 데이터 셋 생성하기

- 데이터 로더 생성하기

2. 모델 학습

- 장치 설정

- 모델 클래스 만들기 + 모델 객체 생성

- loss함수, optimizer 객체 생성

- train 및 validation 루프 함수 생성

- epoch 설정 및 루프 설정

3. 테스트 

- 테스트 데이터 로더 생성하기

- 테스트 실행 함수 생성하기 + 테스트 실행

 

4. 모델 저장

- 모델 가중치와 매개변수만 저장 + 불러오기

- 모델 저장 + 불러오기


1. 데이터 전처리

    1. 데이터 EDA, 필요없는 데이터 제거, 라벨인코딩, 원핫 인코딩 등 데이터 가공 

    2. 데이터를 알맞게 변환할 transform 객체 생성

transform_train = transforms.Compose([ # 데이터에 여러효과를 순서대로 적용
    transforms.Resize((256, 256)), # 크기 동일하게 맞추기
    transforms.RandomHorizontalFlip(), # 랜덤으로 좌우반전. 확률 50% (데이터 증강)
    transforms.ToTensor(), # 텐서형으로 바꾸기 + "데이터를 0-1 사이로 정규화 해줌!" + RGB 이미지(H, W, C)를 -> 변환 후 Tensor (C, H, W)가 됨. H:높이, W: 가로, C: 채널
    transforms.Normalize(  # tensor 정규화된 값의 평균과 표준편차를 0.5로 표준화. [0.0 ~ 1.0] -> [-1.0 ~ 1.0]변환. 이유? Relu 사용시 기울기를 좀 더 살릴 수 있다.
        mean=[0.5, 0.5, 0.5],
        std=[0.5, 0.5, 0.5]
    )
])
# Relu 예시
# x = [0.2, 0.4, 0.7, 0.9] : 모두 양수이므로 Relu를 그대로 통과 [0.2, 0.4, 0.7, 0.9]
# x = [-0.8, -0.2, 0.3, 0.9] : Relu 통과 후 [0.0, 0.0, 0.3, 0.9]

transform_test = transforms.Compose([ # -> test는 데이터 증강을 하지 않아야 함. 원래 모습대로 시험 봐야하니까
    transforms.Resize((256, 256)),
    transforms.ToTensor(),
    transforms.Normalize(
        mean=[0.5, 0.5, 0.5],
        std=[0.5, 0.5, 0.5]
    )
])

    3. 데이터를 Train, Validation, Test 용으로 분류 + 데이터 셋 생성하기

train_dataset = datasets.ImageFolder( # 이미지를 불러들여서 데이터셋을 만들어주는 메소드
    root='train/', # 폴더경로 내부 데이터 모두 가져옴
    transform = transform_train # 가져오면서 transform방법은 transform_train이걸 적용할 것이다.
)
dataset_size = len(train_dataset)
train_size = int(dataset_size * 0.8)
val_size = dataset_size - train_size

train_dataset, val_dataset = random_split(train_dataset, [train_size, val_size]) # random_split: 파이토치의 split 함수. (데이터, [나눌 사이즈])
test_dataset = datasets.ImageFolder(
    root='test/',
    transform=transform_test
)

    4. 데이터 로더 생성하기

# 미니배치용 dataloader 만들기
train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_dataloader = DataLoader(val_dataset, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_dataset, batch_size=64, shuffle=False) # 정답은 셔플이 필요 없음

 

2. 모델 학습

    1. 장치 설정

# 장치 확인
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(device)

    2. 모델 클래스 만들기 + 모델 객체 생성

class ConvNeuralNetwork(nn.Module):
    def __init__(self):
        super(ConvNeuralNetwork, self).__init__()  # super(ClassName, self).__init__() 은 파이썬 2버전 표현법. 파이썬 3버전 표현인 super().__init__() 과 동일하다.
        self.flatten = nn.Flatten()
        self.classifier = nn.Sequential(
            nn.Conv2d(1, 28, kernel_size=3, padding='same'),
            nn.ReLU(),

            nn.Conv2d(28, 28, kernel_size=3, padding='same'),
            nn.ReLU(),

            nn.MaxPool2d(kernel_size=2),
            nn.Dropout(0.25),

            nn.Conv2d(28, 56, kernel_size=3, padding='same'),
            nn.ReLU(),

            nn.Conv2d(56, 56, kernel_size=3, padding='same'),
            nn.ReLU(),

            nn.MaxPool2d(kernel_size=2),
            nn.Dropout(0.25)
        )
        self.Linear = nn.Linear(56 * 7 * 7, 3)

    def forward(self, x):
        x = self.classifier(x)
        x = self.flatten(x)
        output = self.Linear(x)
        return output
model = ConvNeuralNetwork().to(device)
print(model)

    3. loss함수, optimizer 객체 생성

loss = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

     4. train 및 validation 루프 함수 생성

def train_loop(train_loader, model, loss_fn, optimizer):
    sum_losses = 0
    sum_accs = 0
    for x_batch, y_batch in train_loader:
        x_batch = x_batch.to(device)
        y_batch = y_batch.to(device)
        y_pred = model(x_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        sum_losses += loss.item()

        y_prob = nn.Softmax(1)(y_pred)
        y_pred_index = torch.argmax(y_prob, axis=1)
        acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
        sum_accs = sum_accs + acc

    avg_loss = sum_losses / len(train_loader)
    avg_acc = sum_accs / len(train_loader)
    return avg_loss, avg_acc

    5. epoch 설정 및 루프 설정

epochs = 50

for i in range(epochs):
    print(f"------------------------------------------------")
    avg_loss, avg_acc = train_loop(loader, model, loss, optimizer)
    print(f'Epoch {i:4d}/{epochs} Loss: {avg_loss:.6f} Accuracy: {avg_acc:.2f}%')
print("Done!")

 

3. 테스트

    1. 테스트 데이터 로더 생성하기

# 테스트 데이터 로드
test_loader = DataLoader(
    dataset=testset,
    batch_size=32, # 시험 배치는 학습 배치와 상관없음
    shuffle=True
)

    2. 테스트 실행 함수 생성하기 + 테스트 실행

def test(model, loader):
    model.eval() # model이 이제 테스트로 작동해서 학습하는 메모리를 꺼버림 (=grad저장 안하게됨)

    sum_accs = 0

    img_list = torch.Tensor().to(device)
    y_pred_list = torch.Tensor().to(device)
    y_true_list = torch.Tensor().to(device) # 텐서형 빈 자료형

    for x_batch, y_batch in loader:
        x_batch = x_batch.to(device)
        y_batch = y_batch.to(device)
        y_pred = model(x_batch)
        y_prob = nn.Softmax(1)(y_pred)
        y_pred_index = torch.argmax(y_prob, axis=1)
        y_pred_list = torch.cat((y_pred_list, y_pred_index), dim=0)
        y_true_list = torch.cat((y_true_list, y_batch), dim=0)
        img_list = torch.cat((img_list, x_batch), dim=0)
        acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
        sum_accs += acc

    avg_acc = sum_accs / len(loader)
    return y_pred_list, y_true_list, img_list, avg_acc
y_pred_list, y_true_list, img_list, avg_acc = test(model, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')

 

4. 모델 저장

   

    1. 모델 가중치와 매개변수만 저장 + 불러오기

# 모델의 가중치와 매개변수만 저장
# 모델의 구조가 저장되지 않으므로 모델 클래스 정의가 없으면 복원할 수 없음
torch.save(model.state_dict(), 'model_weights.pth')
model2 = ConvNeuralNetwork().to(device) # 클래스로 새로운 객체를 만듦
print(model2)
model2.load_state_dict(torch.load('model_weights.pth'))
y_pred_list, y_true_list, img_list, avg_acc = test(model2, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')

 

  2. 모델 저장 + 불러오기

# 모델 전체를 저장
# 모델 클래스와 가중치가 함께 저장되므로, 복원 시 모델 구조를 별도로 정의할 필요 없음
torch.save(model, 'model.pth')
model3 = torch.load('model.pth', weights_only=False) # weights_only=False : 가중치만 불러오지 않겠다. model 전체를 불러오겠다
y_pred_list, y_true_list, img_list, avg_acc = test(model3, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')