1. 모델 구축
앞 선 포스팅까지 데이터를 수집하여 모델이 학습시키기 좋은 형태로 데이터의 형태를 바꿔주는 작업을 진행했다. 지금 부터 할 것은 모델을 구축하는 일이다. 나는 모델을 MLP(multi Layer Perceptron) 다층 신경망 모델을 선택했다.
MLP란 입력층과 중간에 숨겨진 은닉층 그리고 결과를 출력하는 출력층으로 구분되고 나는 은닉층의 개수를 2개로 진행했다.
class Regressor(nn.Module):
def __init__(self):
super().__init__() # 모델 연산 정의
self.fc1 = nn.Linear(11, 50, bias=True) # 입력층(11) -> 은닉층1(50)으로 가는 연산
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
#drop out 은 과적합(overfitting)을 방지하기 위해 노드의 이부를 배제하고 계산하는 방식이기 때문에 출력층에 사용하면 안된다.
다음이 MLP의 모델이다. 처음에 들어오면 fc1에서 입력을 받는다. 변수의 개수가 11개이므로 11개에서 50개의 결과를 내보낸다. 그 후 Fc2에서는 50개를 다시 30개로 줄이는 과정을 Linear함수를 이용해 진행한다.
그 후 과적합을 방지하기 위해 Dropout함수를 이용해서 한 epoch를 돌 때마다 20%의 비율로 랜덤하게 노드를 없애는 과정을 진행했다. 나는 Linear함수를 통과한 후 활성화 함수로 Relu를 이용하여 진행했다. 왜냐하면 Relu 함수는 음수의 영역에서는 0이다가 양수의 영역에서는 linear하게 증가하는 nonLinear함수인데 non Linear함수를 곱해주지 않으면 결국 Linear해지는 문제도 존재하고 집 값이라는 데이터의 특성 상 음수가 존재하지 않기 때문에 음수의 함수값이 존재하지 않는 Relu함수가 적합하다는 생각이 들었다.
2. 모델, 손실함수, 최적화 방법 선언
한 번 학습을 시키면 Loss(정답과이 차이)를 구해 그 Loss를 줄이는 방향으로 학습을 시켜야한다. 이 때 필요한 것이 일단 Loss를 구해줄 Loss함수 그 후 Loss를 줄이는 과정인 최적화 방법을 선택하는 것이다.
나는 MSELoss로 Loss를 구하기로 했다. MSE란 Mean Squared Error의 약자로 예측 값 - 정답 값을 제곱한 Loss를 의미한다.
최적화 방법으로는 요즘 제일 핫 한 Adam을 선택했고 learning rate로는 0.00.1.을 넣어주었다. learning rate는 아주 중요한 하이퍼 파라미터의 일종으로 Loss함수를 얼만 큼의 간격으로 이동하며 최소 값을 찾을지 정해주는 비율에 해당한다.
model = Regressor()
criterion = nn.MSELoss() # hyper parameter 활성화 함수 종류
#lr == learning rate
#weight_decay는 L2 정규화에서의 penalty 정도
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-7)#하이퍼 파라미터 lr weight decay optimizer 종류
3. 학습
나는 각 학습마다 발생하는 Loss를 알기위해 우선 Loss를 저장해줄 빈 배열을 선언했다.
loss_ = [] # 그래프를 그리기 위한 loss 저장용 리스트
n = len(trainLoader)
for epoch in range(400): # 400번 학습을 진행한다.
running_loss = 0.0
for i, data in enumerate(trainLoader, 0): # 무작위로 섞인 32개 데이터가 있는 배치가 하나 씩 들어온다.
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를 더한다.
print(running_loss)
loss_.append(running_loss/n) # MSE(Mean Squared Error) 계산
print('Finished Training')
그 후 epoch를 돌면서 학습을 시키는데 1batch사이즈를 아까 32로 설정해주었고 enumerate for문을 이용해서 앞선 포스팅에서 DataLoader함수를 이용해 생성한 배치 사이즈의 데이터들을 불러오고 model에 넣어주었다. 그 후 앞서 MSELoss로 설정해준 criterion함수를 이용해 loss를 구하고 optimizer adam에 넣어주어 다시 역전파를 시켜 가중치를 조절해 loss함수가 최소가 되도록 조절해준다.
그 후 loss를 출력해보면 다음과 같다.
경향성이 보이는가 많이 줄어들지는 않지만 결국 Loss가 줄어드는 방향으로 나아가고 있다. 이러한 경우 학습이 잘 되고 있는 것이다.
plt.plot(loss_)
plt.title("Training Loss")
plt.xlabel("epoch")
plt.show()
matplotlib을 이요해 아까 누적하여 저장한 loss배열을 그래프를 통해 나타내주었다.
Loss가 줄어드는 것으로 보아 학습이 잘 진행되고 있다.
5. 모델 평가
#predictions = torch.tensor([]) # 예측 값을 저장하는 tensor
#actual = torch.tensor([]) # 실제 정답을 저장하는 tensor
def evaluation(dataLoader):
predictions = torch.tensor([]) # 예측 값을 저장하는 tensor
actual = torch.tensor([]) # 실제 정답을 저장하는 tensor
with torch.no_grad():
model.eval() #평가를 할 때는 .eval()을 반드시 사용
for data in dataLoader:
inputs, values = data
outputs = model(inputs)
print(outputs)
predictions = torch.cat((predictions, outputs), 0) #cat을 통해 예측값을 누적
actual = torch.cat((actual, values), 0)
predictions = predictions.numpy()
actual = actual.numpy()
#print(predictions)
#print(actual)
rmse = np.sqrt(mean_squared_error(predictions, actual)) # sklearn을 이용해서 RMSE
return rmse
evaluation(trainLoader)
#평가 시 , eval()
#평가 시에는 온전한 모델로 평가를 해야하는데, .eval()이 아닌, .train()의 경우 드랍아웃이 활성화 되어있다.
#따라서 드랍아웃이나 배치 정규화 등과 같이 학습 시에만 사용하는 기술들을 평가 시에는 비활성화 해야한다.
평가 class를 왜 따로 만들어야하냐면 dropout과 같이 학습에만 필요한 과정들을 비활성화 해주기 위함이다. 이 것이 평가의 결과에는 영향을 미치지 않기 때문이다. .eval함수를 이용해 진행해주었고 모델로 예측한 prediction값들을 cat함수를 통해 누적 시켜주었다. 또한 실제 정답인 actual값도 cat함수를 통해 누적해주었다. 그 후 rmse를 구해 결과를 보면 다음과 같다.
train_rmse = evaluation(trainLoader) #학습 데이터의 RMSE
vali_rmse = evaluation(validationLoader) #validation의 RMSE
test_rmse = evaluation(testloader)
print("Train RMSE: ", train_rmse)
print("Vali RMSE: ", vali_rmse)
print("Test RMSE: ", test_rmse)
train data에서 0.012오차
validation data에서 0.014오차
test data에서 0.015오차 가 발생했다. 생각보다 결과가 잘나와서 만족스러웠다.
'파이썬 > ai' 카테고리의 다른 글
[ai, python] - Image분류 by CNN (2) | 2022.05.29 |
---|---|
[ai, python] Neural Network (1) | 2022.05.29 |
[ai, python]인천광역시 집 값 예측 machine Learning - 데이터 전처리 3 in Colab (0) | 2022.05.21 |
[ai, python] 인천광역시 집값 예측 프로젝트- 데이터 전처리 2 (0) | 2022.05.18 |
[ai, Python] 인천시 집 값 예측 프로그램-1(데이터 수집 및 전처리1) (2) | 2022.05.17 |
댓글