MLE는 '최대 우도 추정'이라고도 한다.
압정을 던졌을 때 앞, 뒤 중 어디로 떨어질 것인지 예측한다고 해보자.
주어진 관측값은 n번 시행했을 때, k번 앞으로 떨어졌다고 한다.
압정던지기는 베르누이 분포를 따르므로 다음과 같이 표현할 수 있다.
여기서 는 조합(combination)기호로 n개 원소중 k개를 순서와 상관없이 조합할 수 있는 경우의 수를 뜻한다. 즉, 는 으로 표현할 수 있다.
로 나타내고, 관측 값을 대입하면 에 대한 함수 를 만들 수 있다. 여기서 MLE란, 의 값이 최대가 되는 를 찾는 것이다.
어떻게 최적의 를 찾을 수 있을까.
바로 함수의 기울기를 이용하는 것이다.
기울기를 이용해 최대를 구하는 과정을 gradient ascent 라고 하며 최소를 찾는 과정은 gradient descent이다.
그럼 descent를 기준으로는 최적의 는 어떻게 갱신될까.
위와 같은 식을 통해 최적은 값을 찾아가게 된다.
는 이후 설명하겠지만 learning rate이고, 이 learning rate에 를 에 대한 미분을 한 값을 곱한 후 기존 에서 빼어준다.
Overfitting이란 주어진 데이터에 대해서만 과도하게 fitting된 상황이다.
MLE를 이용하여 주어진 데이터를 가장 잘 설명하는 모델을 찾다보면 overfitting을 만나게 된다.
그렇다면 어떻게 overfitting을 확인하고 피할 수 있을까.
어떠한 모델을 훈련 시킬 때 우리는 데이터를 Train data와 Test data로 나눈다. Train data는 모델을 훈련시킬 때만 사용을 하고, Test data로 훈련이 된 모델을 평가한다.
하지만, 모델이 Train data에 overfitting된다면 Test data에는 모델이 제 성능을 낼 수 없다. Test data는 말 그래로 평가 하는데에만 사용해야한다. Test data를 training(학습)에 사용할 수 없다. 따라서, Validation data라는 개념이 사용된다.
전체 dataset을 train, validation, test로 나누어 사용한다. 이때, train data는 학습하는데 사용되고, validation data는 모델의 검증에 사용, test data는 최종 모델을 평가하는데 사용한다.
뿐만아니라 더 많은 데이터, features를 줄이거나, Regularization을 사용하여 overfitting을 막을 수 있다.
Regularizaion은 다음과 같은 것들이 존재한다.
이러한 기법들을 적용하여 DNN을 훈련시키는 과정은 다음과 같다.
신경망의 구조를 설계한다.
모델을 훈련하고 overfitting을 확인한다.
a. overfitting이 아니라면 모델 사이즈를 증가시킨다.(deeper and wider)
b. overfitting이라면 Regularization을 적용한다.(Dropout, Batch Norm 등)
2를 반복한다.
Training set과 Test set을 나누는 실습을 한다.
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
torch.manual_seed(1)
Dataset
아래와 같이 train, test set으로 분리한다.
x_train = torch.FloatTensor([[1, 2, 1],
[1, 3, 2],
[1, 3, 4],
[1, 5, 5],
[1, 7, 5],
[1, 2, 5],
[1, 6, 6],
[1, 7, 7]])
y_train = torch.LongTensor([2, 2, 2, 1, 1, 1, 0, 0])
x_test = torch.FloatTensor([[2, 1, 1], [3, 1, 2], [3, 3, 4]])
y_test = torch.LongTensor([2, 2, 2])
Moduel
softmax분류기를 이용한다. nn.Module
을 상속해 사용한다.
class SoftmaxClassifierModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(3, 3)
def forward(self, x):
return self.linear(x)
model = SoftmaxClassifierModel()
optimizer = optim.SGD(model.parameters(), lr=0.1)
def train(model, optimizer, x_train, y_train):
nb_epochs = 20
for epoch in range(nb_epochs):
prediction = model(x_train)
cost = F.cross_entropy(prediction, y_train)
optimizer.zero_grad()
cost.backward()
optimizer.step()
print('Epoch {:4d}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, cost.item()
))
train(model, optimizer, x_train, y_train)
'''
Epoch 0/20 Cost: 2.203667
Epoch 1/20 Cost: 1.199645
Epoch 2/20 Cost: 1.142985
Epoch 3/20 Cost: 1.117769
Epoch 4/20 Cost: 1.100901
Epoch 5/20 Cost: 1.089523
Epoch 6/20 Cost: 1.079872
Epoch 7/20 Cost: 1.071320
Epoch 8/20 Cost: 1.063325
Epoch 9/20 Cost: 1.055720
Epoch 10/20 Cost: 1.048378
Epoch 11/20 Cost: 1.041245
Epoch 12/20 Cost: 1.034285
Epoch 13/20 Cost: 1.027478
Epoch 14/20 Cost: 1.020813
Epoch 15/20 Cost: 1.014279
Epoch 16/20 Cost: 1.007872
Epoch 17/20 Cost: 1.001586
Epoch 18/20 Cost: 0.995419
Epoch 19/20 Cost: 0.989365
'''
Test
아래 정확도는 0%로 overfitting 된 상황이라는 것을 짐작해볼 수 있다.
def test(model, x_test, y_test):
prediction = model(x_test)
predicted_classes = prediction.max(1)[1]
correct_count = (predicted_classes == y_test).sum().item()
cost = F.cross_entropy(prediction, y_test)
print('Accuracy: {}% Cost: {:.6f}'.format(
correct_count / len(y_test) * 100, cost.item()
))
test(model, x_test, y_test)
'''
Accuracy: 0.0% Cost: 1.425844
'''
MLE는 Gradient Descent(혹은 Ascent)로 수행한다.
Gradient Descent를 기준으로 아래식을 통해 수행하고
**
위 최적화 과정을 통해 주어진 데이터 를 가장 잘 설명하는 를 찾아간다.
이때, 를 우리는 learning rate라고 부른다. learning rate는 최적화를 위해 이동하는 거리의 크기라고 생각하면 쉽다. 또한, learning rate는 인간이 직접 설정해주어야 하는 Hyper Parameter
이다.
learning rate가 클 경우 발산할 수 있다.
model = SoftmaxClassifierModel()
optimizer = optim.SGD(model.parameters(), lr=1e5)
train(model, optimizer, x_train, y_train)
'''
Epoch 0/20 Cost: 3.187324
Epoch 1/20 Cost: 1100707.375000
Epoch 2/20 Cost: 2482261.000000
...
Epoch 17/20 Cost: 1265073.750000
Epoch 18/20 Cost: 1686645.000000
Epoch 19/20 Cost: 484999.625000
'''
model = SoftmaxClassifierModel()
optimizer = optim.SGD(model.parameters(), lr=1e-10)
train(model, optimizer, x_train, y_train)
'''
Epoch 0/20 Cost: 1.341574
Epoch 1/20 Cost: 1.341574
Epoch 2/20 Cost: 1.341574
...
Epoch 17/20 Cost: 1.341574
Epoch 18/20 Cost: 1.341574
Epoch 19/20 Cost: 1.341574
'''
model = SoftmaxClassifierModel()
optimizer = optim.SGD(model.parameters(), lr=1e-1)
train(model, optimizer, x_train, y_train)
'''
Epoch 0/20 Cost: 2.939317
Epoch 1/20 Cost: 1.887239
Epoch 2/20 Cost: 1.055398
...
Epoch 17/20 Cost: 0.855000
Epoch 18/20 Cost: 0.850961
Epoch 19/20 Cost: 0.847010
'''
처음 학습 시 적당한 learning rate를 선택하여 학습하고 cost값을 확인하며 발산한다면 learning rate를 감소시키고, 학습이 더디다면 learning rate를 증가시키는 등의 방법을 이용하여 learning rate를 설정하면 된다.
Data Preprocessing(데이터 전처리)는 학습을 더 쉽게 해주기 위한 과정이다. 아래 데이터를 살펴보면 3개의 feature를 이용해 regression시키는 task이다.
x_train = torch.FloatTensor([[73, 80, 75],
[93, 88, 93],
[89, 91, 90],
[96, 98, 100],
[73, 66, 70]])
y_train = torch.FloatTensor([[152], [185], [180], [196], [142]])
이때, 데이터 전처리를 해주면 학습이 더욱 수월해진다. 여기서는 Standardization을 이용해 preprocessing 시켜본다. Standardization은 아래 식과 같다.
여기서 는 표준편차, 는 평균이다. 공식을 이용해 위 데이터를 Standardization 시키면 아래와 같다.
mu = x_train.mean(dim=0)
sigma = x_train.std(dim=0)
norm_x_train = (x_train-mu)/sigma
print(norm_x_train)
'''
tensor([[-1.0674, -0.3758, -0.8398],
[ 0.7418, 0.2778, 0.5863],
[ 0.3799, 0.5229, 0.3486],
[ 1.0132, 1.0948, 1.1409],
[-1.0674, -1.5197, -1.2360]])
'''
그렇다면 전처리된 데이터를 이용해 학습해보자. nn.Module
을 상속받는 MultivariateLinearRegressionModel
을 설계하여 사용한다.
class MultivariateLinearRegressionModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(3, 1)
def forward(self, x):
return self.linear(x)
model = MultivariateLinearRegressionModel()
optimizer = optim.SGD(model.parameters(), lr=1e-1)
학습을 담당하는 train
함수를 구현해 사용한다. 이때, regression task 이므로 cost function 으로 MSE
를 사용한다.
def train(model, optimizer, x_train, y_train):
nb_epochs = 20
for epoch in range(nb_epochs):
prediction = model(x_train)
cost = F.mse_loss(prediction, y_train)
optimizer.zero_grad()
cost.backward()
optimizer.step()
print('Epoch {:4d}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, cost.item()
))
train(model, optimizer, norm_x_train, y_train)
'''
Epoch 0/20 Cost: 29474.621094
Epoch 1/20 Cost: 18722.042969
Epoch 2/20 Cost: 11941.125000
Epoch 3/20 Cost: 7630.646973
...
Epoch 16/20 Cost: 23.729731
Epoch 17/20 Cost: 15.416880
Epoch 18/20 Cost: 10.089418
Epoch 19/20 Cost: 6.672885
'''
데이터 전처리는 이외에도 여러 방법이 있다. 각 데이터와 상황에 맞게 전처리 하는 것이 가장 중요하다.