파이토치

[파이토치] Neural Networks

민선민선 2022. 2. 14. 11:36

인공 신경망은 사람의 신경망을 모사하여 만든 예측 도구이다. 기본적으로 하나의 레이어에 다수의 노드를 가지고 있으며 여러 개의 레이어가 쌓인 신경망을 깊은 신경망이라고 한다. 이 때, 깊은 신경망을 이용하여 모델을 학습 시키는 방법을 딥러닝이라고 한다.

인공 신경망의 구성

import pandas as pd # 데이터프레임 형태를 다룰 수 있는 라이브러리
import numpy as np
from sklearn.model_selection import train_test_split # 전체 데이터를 학습 데이터와 평가 데이터로 나눈다.

# ANN
import torch
from torch import nn, optim # torch 내의 세부적인 기능을 불러온다. (신경망 기술, 손실함수, 최적화 방법 등)
from torch.utils.data import DataLoader, Dataset # 데이터를 모델에 사용할 수 있도록 정리해 주는 라이브러리
import torch.nn.functional as F # torch 내의 세부적인 기능을 불러온다. (신경망 기술 등)

# Loss
from sklearn.metrics import mean_squared_error # Regression 문제의 평가를 위해 MSE(Mean Squared Error)를 불러온다.

# Plot
import matplotlib.pyplot as plt # 시각화 도구

# 데이터 불러오기

df = pd.read_csv('./data/reg.csv', index_col=[0])

# 데이터를 넘파이 배열로 만들기
X = df.drop('Price', axis=1).to_numpy() # 데이터프레임에서 타겟값(Price)을 제외하고 넘파이 배열로 만들기
Y = df['Price'].to_numpy().reshape((-1,1)) # 데이터프레임 형태의 타겟값을 넘파이 배열로 만들기
 
# 텐서 데이터로 변환하는 클래스
class TensorData(Dataset):

    def __init__(selfx_datay_data):
        self.x_data = torch.FloatTensor(x_data)
        self.y_data = torch.FloatTensor(y_data)
        self.len = self.y_data.shape[0]

    def __getitem__(selfindex):

        return self.x_data[index], self.y_data[index] 

    def __len__(self):
        return self.len
# 전체 데이터를 학습 데이터와 평가 데이터로 나눈다.
# test size를 0.5로 설정한다.
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.5)

# 학습 데이터, 시험 데이터 배치 형태로 구축하기
trainsets = TensorData(X_train, Y_train)
trainloader = torch.utils.data.DataLoader(trainsets, batch_size=32, shuffle=True)

testsets = TensorData(X_test, Y_test)
testloader = torch.utils.data.DataLoader(testsets, batch_size=32, shuffle=False)
class Regressor(nn.Module):
    def __init__(self):
        super().__init__() # 모델 연산 정의
        self.fc1 = nn.Linear(1350, bias=True# 입력층(13) -> 은닉층1(50)으로 가는 연산
        self.fc2 = nn.Linear(5030, bias=True# 은닉층1(50) -> 은닉층2(30)으로 가는 연산
        self.fc3 = nn.Linear(301, bias=True# 은닉층2(30) -> 출력층(1)으로 가는 연산
        self.dropout = nn.Dropout(0.2# 연산이 될 때마다 20%의 비율로 랜덤하게 노드를 없앤다.

    def forward(selfx): # 모델 연산의 순서를 정의
        x = F.relu(self.fc1(x)) # Linear 계산 후 활성화 함수 ReLU를 적용한다.  
        x = self.dropout(F.relu(self.fc2(x))) # 은닉층2에서 드랍아웃을 적용한다.(즉, 30개의 20%인 6개의 노드가 계산에서 제외된다.)
        x = F.relu(self.fc3(x)) # Linear 계산 후 활성화 함수 ReLU를 적용한다.  
      
        return x
model = Regressor()
criterion = nn.MSELoss()
 
# weight_decay는 L2 정규화에서의 penalty 정도를 의미한다.
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-7) # 모델에 있는 파라메터를 업데이트한다.
loss_ = [] # 그래프를 그리기 위한 loss 저장용 리스트 
n = len(trainloader)

for epoch in range(400): # 400번 학습을 진행한다.

    running_loss = 0.0 # 배치마다 loss를 더한다음에 마지막에 n으로 나눈다.
    
    for i, data in enumerate(trainloader, 0): # 무작위로 섞인 32개 데이터가 있는 배치가 하나씩 들어온다.
        # 마지막 배치에 32개가 안되더라도 들어온다.
        inputs, values = data # data에는 X, Y가 들어있다.

        optimizer.zero_grad() # 최적화 초기화
        
        outputs = model(inputs) # 모델에 입력값 대입 후 예측값 산출
        # output 1개 짜리가 32개가 나온다.
        loss = criterion(outputs, values) # 손실 함수 계산
        loss.backward() # 손실 함수 기준으로 역전파 설정 
        optimizer.step() # 역전파를 진행하고 가중치 업데이트
        
        running_loss += loss.item() # epoch 마다 평균 loss를 계산하기 위해 배치 loss를 더한다.

    loss_.append(running_loss/n) # MSE(Mean Squared Error) 계산

        
print('Finished Training')
plt.plot(loss_)
plt.title("Training Loss")
plt.xlabel("epoch")
plt.show()

 

def evaluation(dataloader):
    
    predictions = torch.tensor([], dtype=torch.float# 예측값을 저장하는 텐서
    actual = torch.tensor([], dtype=torch.float# 실제값을 저장하는 텐서
        
    with torch.no_grad(): # 평가를 할 때에는 최적화 하지않기때문에 grad를 없애고 계산한다.
        model.eval() # 평가를 할 때에는 .eval() 반드시 사용해야 한다.
        # 평가 시 .eval()을 사용해야 하는 이유
        # 평가 시에는 온전한 모델로 평가를 해야하는데 .eval()이 아닌 .train()인 경우 드랍아웃이 활성화 되어있다.
        # 따라서 드랍아웃이나 배치 정규화 등과 같이 학습 시에만 사용하는 기술들을 평가시에는 비활성화 해야한다.
        for data in dataloader:
            inputs, values = data
            outputs = model(inputs)

            predictions = torch.cat((predictions, outputs), 0# cat을 통해 예측값을 누적
            actual = torch.cat((actual, values), 0# cat을 통해 실제값을 누적
    
    predictions = predictions.numpy() # 넘파이 배열로 변경
    actual = actual.numpy() # 넘파이 배열로 변경
    rmse = np.sqrt(mean_squared_error(predictions, actual)) # sklearn을 이용하여 RMSE 계산
    
    return rmse  
train_rmse = evaluation(trainloader) # 학습 데이터의 RMSE
test_rmse = evaluation(testloader) # 시험 데이터의 RMSE

print("Train RMSE: ",train_rmse)
print("Test RMSE: ",test_rmse)