인공신경망은 사람의 신경(뉴런)을 모사하여 만든 예측 도구.
레이어 안에 많은 노드를 가지고 있고, 이런 레이어가 쌓이면 깊은(Deep) 신경망이라고 한다.
이렇게 깊게 쌓인 신경망을 이용하여 모델을 학습 시키는 방법이 Deep Learning 이다 !
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split # 학습 데이터/평가 데이터 나누기
# ANN (Arificial Neural Networks)
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 문제의 평가용
# Plot
import matplotlib.pyplot as plt # 시각화 도구
다음과 같은 데이터가 있다고 가정할때 (변수 13개, 타겟 1개)
X = df.drop('Price', axis=1).to_numpy() # 데이터프레임에서 타겟값(Price)을 제외하고 넘파이 배열로
Y = df['Price'].to_numpy().reshape((-1,1)) # 데이터프레임 형태의 타겟값을 넘파이 배열로
class TensorData(Dataset):
def __init__(self, x_data, y_data):
self.x_data = torch.FloatTensor(x_data)
self.y_data = torch.FloatTensor(y_data)
self.len = self.y_data.shape[0]
def __getitem__(self, index):
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(13, 50, bias=True) # 입력층(13) -> 은닉층1(50)으로 가는 연산 # 변수 13개
self.fc2 = nn.Linear(50, 30, bias=True) # 은닉층1(50) -> 은닉층2(30)으로 가는 연산
self.fc3 = nn.Linear(30, 1, bias=True) # 은닉층2(30) -> 출력층(1)으로 가는 연산
self.dropout = nn.Dropout(0.2) # 연산이 될 때마다 20%의 비율로 랜덤하게 노드를 없앤다.
def forward(self, x): # 모델 연산의 순서를 정의
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
# 주의 사항
# F.ReLu랑 nn.ReLu랑 같은 결과
# 드랍아웃은 과적합(overfitting)을 방지하기 위해 노드의 일부를 배제하고 계산하는 방식
# 따라서, 절대로 출력층에 사용해서는 안 된다!!!!!
model = Regressor()
criterion = nn.MSELoss()
# lr은 학습률
# 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
for i, data in enumerate(trainloader, 0): # 무작위로 섞인 32개 데이터가 있는 배치가 하나 씩 들어온다.
# enumerate는 index를 뽑기 위한 기능 (사용은 X)
inputs, values = data # data에는 X, Y가 들어있다.
optimizer.zero_grad() # 최적화 초기화 (중요!!)
outputs = model(inputs) # 모델에 입력값 대입 후 예측값 산출
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(): # 평가할때는 gradient 필요 없음
model.eval() # 평가를 할 때에는 .eval() 반드시 사용해야 한다. (중요)
for data in dataloader:
inputs, values = data
outputs = model(inputs)
predictions = torch.cat((predictions, outputs), 0) # cat을 통해 예측값을 누적
actual = torch.cat((actual, values), 0) # cat을 통해 실제값을 누적
rmse = np.sqrt(mean_squared_error(predictions, actual)) # sklearn을 이용하여 RMSE 계산
return rmse
# 평가 시 .eval()을 사용해야 하는 이유
# 평가 시에는 온전한 모델로 평가를 해야하는데 .eval()이 아닌 .train()인 경우 드랍아웃이 활성화 되어 있다.
# 따라서 드랍아웃이나 배치 정규화 등과 같이 학습 시에만 사용하는 기술들을 평가 시에는 비활성화 해야만 한다.
train_rmse = evaluation(trainloader) # 학습 데이터의 RMSE
test_rmse = evaluation(testloader) # 시험 데이터의 RMSE
print("Train RMSE: ",train_rmse)
print("Test RMSE: ",test_rmse)