다음과 같은 과정을 거친다.
→ 데이터 준비 후 누락 값 이상치 제거 등의 과정을 거친다.
→ 신경망을 생성한다. 일반적으로 Hidden Layer의 개수가 많을수록 성능이 좋아지지만 과적합이 발생할 확률이 높다.
또한 활성화 함수, 목적 함수, Optimizer등을 선택한다.
Forward propagation 과정에서 우리는 우리가 준비한 데이터를 input layer에 값으로 집어넣고 여러 가중치와 hidden layer에 활성화 함수를 거치면서 우리는 Output layer에 도달하게 된다.
이때 목적함수를 최적화 하는 방향으로 가중치를 업데이트 하는 backward propagation 과정을 거치면서 가중치를 업데이트 한다.
[Multi-Layer Perceptrons]
→ MLP는 Activation Function을 갖는 Hidden Layer가 하나 이상인 Neural Network를 의미한다.
활성화 함수의 목적은 전체 Network에 Non-Linearity를 부여하는데 있다. 다음의 예제를 살펴보자.
위와 같은 경우 Hidden Layer를 추가한 의미가 없어진다.
Hidden Layer를 추가하지 않아도 결과 값을 표현하는데 문제가 없고, 선형적인 예측만 가능한 것을 볼 수 있다.
따라서 이를 해결하기 위해서 여러 Activation Function을 추가하는데, 몇 가지 Activation Function을 살펴보겠다.
1) Sigmoid
단점 :
가. Saturation 현상
좌우로 접선의 기울기가 0이 되는 지점이 -6과 +6만 되어도 시작이 되기 때문에
위처럼 BackPropagation이 진행될 때, Gradient Vanishing 현상이 발생한다.
나. exp연산이 들어가므로 연산이 느리다.
다. Zig Zag 업데이트 현상
이렇게 0이 중심(**zero-centered**)가 아닌 활성화 함수는 Zig Zag현상에 의해 학습 속도가 느리다.
2) Tanh(Hyperbolic Tangent)
장점: 결과 값이 -1~1 사이 이기 때문에 ZigZag현상이 덜하다.
단점: 변함없이 Saturation 현상이 있어 w 업데이트가 멈출 수 있다.
3) ReLU(Rectified Linear Unit):
장점:
단점:
학습을 통해 최적화 시키고자 하는 함수로써, DeepLearning에서는 일반적으로 학습을 통해 Loss를 최적화 시키려는 작업을 수행하고, 이때의 Loss를 계산하기 위한 함수를 Objective Function혹은 Loss Function 이라고 한다.
여러 Loss Function이 있고, Loss Function에 따라서 모델의 성능 향상이 일어나기도 한다.
위에서 언급했듯 Loss Function을 최적화 시키는 방향으로 학습을 진행하는데, 이때 최적화 알고리즘을 Optimizer라고 한다.
핵심적인 optimizer는 다음과 같다.
1) Gradient Descent
위의 사진에서 weight에 따른 loss function을 볼 수 있다. Backpropagation과정에서 우리는 모델의 weight를 loss function을 최적화 하는 방향으로 업데이트 시켜야 하는데, 이때 loss function을 w로 편미분을 한 것이 바로 Gradient라고 정의하고,
한번에 다가갈 step을 learning rate라고 한다.
만약 learning rate가 이상적이라면 아래와 같이 수렴하겠지만
Learning rate가 크다면 다음과 같이 진행돼서 minimum값에 수렴하지 못하는 상황이 발생한다.
반대로 Learning Rate가 작다면 수렴하긴 하지만 최종 값으로 찾아갈 때까지 너무 많은 훈련을 수행해야 한다. 따라서 학습 속도가 느려진다.
2) Stochastic Gradient Descent(SGD)
우리의 데이터가 100만개 있다고 가정해보자.
우리는 이 데이터를 한 sample씩 Neural Net에 집어넣을 수 있다.
하지만 이렇게 되면 100만개의 데이터를 모두 처리하는데 시간이 많이 걸리고 비효율이 따른다.
따라서 우리는 Stochastic이라는 말을 붙여서 이중에 랜덤으로 몇 개의 Sample을 선택해서 최솟값으로 수렴하도록 하는 것이다.
랜덤으로 선택하는 방법은 단순하게 데이터 Sample을 섞어주면 된다.
확률적 경사 하강법은 극단적으로 노이즈가 많을 수 있지만 평균적으로는 좋은 방향으로 가게 된다.
위와 같이 Local Minimum에 빠지는 것을 막아 줄 수도 있다.
3) Full-Batch Gradient Descent
Batch의 뜻은 다음과 같다.
Full-Batch Gradient Descent는 행렬을 사용하여서 처리하는데, 하나의 가중치 Set이 있고,
이 가중치에 대해서 모든 Sample로 이루어진 1 Batch(즉, 행렬)를 입력으로 넣게 된다.
이렇게 행렬 계산을 통해 각 Sample마다 예측 값을 구해낼 수 있고, 구해진 예측 값들을 하나의 Loss로 만드는 과정에서 Sum이 사용되고, 이렇게 구해진 Loss를 BackPropagation시킨다.
아래는 배치 경사하강법을 잘 설명한 동영상이다.
[출처][https://www.youtube.com/watch?v=mccscAH2kkk](https://www.youtube.com/watch?v=mccscAH2kkk)
위와 같이 모든 데이터를 하나의 Batch로 사용해서, Training의 1Epoch을 수행하는 것을 Full-Batch Gradient Descent라고 한다.
한번의 Epoch에 우리가 가지고 있는 모든 샘플을 사용하므로 데이터의 일관성이 유지 돼서 Smooth하게 Cost Function에 최적 값에 수렴 가능하다.
하지만 데이터가 큰 경우에는 너무 많은 Computing 자원을 소모하게 되는데 따라서 생각해 낸 것이 Mini-Batch GD이다.
4) Mini Batch Gradient Descent
Mini Batch Gradient Descent는 Stochastic Gradient Descent와 Batch Gradient Descent의 절충안으로써, Batch Size를 정하여서, 작은 Size의 Batch를 여러 개 만든 후 해당 Batch를 이용해, Gradient Descent를 진행한다.
Pytorch에서 채택하는 loss.backward()를 사용할 때, 우리는 Batch Size를 입력하여 만든 DataLoader를 이용해서 Gradient Descent를 진행한다.
Mini Batch는 Full Batch보단 덜 Smooth하게 수렴하는 것을 알 수있다.
보통 Batch Size는 2의 n제곱을 사용한다.
💡 그런데, 이런 의문이 들었다 Batch GD와 Mini-Batch GD 둘 다 1 Epoch에 모든 데이터를 사용하는 것은 마찬가지인데, 그러면 데이터 연산의 총 양은 둘 다 같은 것 아닌가? Mini-Batch GD가 Batch GD보다 연산 속도가 빠르다는 것의 의미가 무엇일까?찾아보니 다음과 같은 설명이 있었다.
→ 실제 우리가 딥러닝 모델을 훈련 시킬때는 DataLoader를 For문을 이용해서 훈련을 시키기 때문에 행렬 연산을 GPU를 이용하여 병렬로 수행 시키지 않지만, 추가적인 설정을 통해 행렬 연산을 병렬로 수행 시킨다면, 속도 상의 이점을 가질 수 있다.
더욱 중요한 이점은 다음과 같다.
→ Mini-Batch GD는 Batch GD와 Stochastic GD의 절충안 인데, Batch GD에서 Local Minimum의 빠질 가능성을 Mini-Batch GD를 사용함으로써 줄여주는 것이다.
→ 추가적으로, 너무 많은 데이터의 경우 Batch GD를 사용하면 메모리에 한번에 안 올라갈 수 있기 때문에 Mini-Batch를 사용하여 이런 위험을 줄일 수 있다.
❗<참고>Pytorch에서 사용하는 Linear Layer와 RNN의 입력 Tensor
보통 pytorch의 RNN모델을 사용하게 되면 Input Tensor의 크기를 (Batch_Size, Sequence_Length,Feature Dimension)으로 사용한다.
반면에 일반적인 Linear Layer를 사용할 때는 2차원 텐서(Batch Size, Input dimension)을 사용하는데,
Pytorch의 Linear Layer는 임의의 Tensor Size를 갖을 수 있다.
예를 들어 Input Size가 (10,3,4)인 Tensor를 Linear(4, 5)에 넣게 되면,
Output Size는 마지막 dimension만 바뀌어서 (10,3,5)로 나오게 되고 이때의 원리는 Pytorch는 (10,3,4)를 10*3 = 30개의 4차원 벡터로 바라본다.
따라서 각각의 벡터에 같은 Weight를 적용한 결과가 나오게 된다.
출처: https://stackoverflow.com/questions/58587057/multi-dimensional-inputs-in-pytorch-linear-method
실습>
RNN에서는 메모리 셀( RNN CELL )을 도입해서 여기에서 나오는 결과값인 (Hidden state)를 사용하여 이전의 결과 값이 다음의 결과의 영향을 주도록 만들었다.
따라서, 시계열 데이터에 적합한 모델로써,
시간에 따라서 입력이 Neural Net에 하나 씩 들어오게 되고 결과적으로는 같은 RNN에 입력이 바뀌어서 들어오는 것이기 때문에 시간에 따라 가중치가 공유된다.
RNN을 위와 같이 표현할 수도 있지만 펼쳐서 표현할 수도 있다
RNN에 대한 수식을 작성해보겠다.
입력 벡터의 차원이 d라고 하고 은닉 상태의 크기를 라고 할 때, 각 벡터와 행렬의 크기는 다음과 같다.
RNN의BackPropagation (BackPropagation Through Time)[BPTT]
💡아래와 같이 표현한 것은 RNN Cell을 시간의 흐름에 따라서 길게 늘어뜨린 것일 뿐이고 실제로는 한 개의 Cell임을 다시 한번 상기하자.
RNN이 시간의 흐름에 따라 입력을 받기 때문에, BackPropagation을 할 때에도 시간의 흐름에 따른 입력을 고려하여서 BackPropagation을 진행해야 한다.
BPTT는 손실 함수를 사용한 모든 출력에서 역방향으로 전파된다.
이때, 시계열의 Sequence 길이가 길어지면 곱해지는 것이 계속해서 많아지게 될 것이고 BackPropagation 과정에서 초반의 입력에 대해서는 가중치가 거의 업데이트 되지 않는 Gradient Vanishing문제가 발생한다.
이를 해결하기 위해 Truncated BPTT와 LSTM등의 아이디어가 제안 됐다.
💡 TBPTT의 핵심 아이디어는 다음과 같다.
예를 들어 5000 Step 앞을 예측한다고 해보자. 그러면, 위에서 언급했던 이유로 Gradient Vanishing 문제가 발생할 것이다. 따라서, 우리는 50 Step씩 100개로 쪼갤 것이다.
원래는 5000 Step을 거쳐서 Weight가 업데이트 돼야 하지만
그렇게 하지 말고 50 Step씩 100개의 그룹으로 쪼갠후
1번째 그룹에서 50번째를 예측한 후 구해진 Error로 Weight를 Estimate 하고
2번째 그룹에서 100번째를 예측한 후 구해진 Error로 Weight 를 Estimate 하는 과정을 거치면서
최종적으로 100번째 그룹에서 Weight의 업데이트를 확정 짓는 과정을 거친다.
이때, 각 그룹의 초기 Hidden State는 이전 그룹의 Last Hidden State와 연결 지어 주는 것이 핵심이다.
한글로 된 블로그나 여러가지를 찾아보면 다음과 같이 돼있고 알아보기 너무 어려웠다.
https://www.youtube.com/watch?v=v5FFzZTivwU
위의 동영상에 설명이 나와있으니 확인해보길 바란다.
아래는 Truncated BPTT 구현의 관련한 Pytorch Forum이다. 여기서 핵심 아이디어는 Forward를 진행하다가 멈추고 싶은 지점에서 반복문으로 멈춘 후 BackPropagation을 진행하는 방식을 사용한다.
https://discuss.pytorch.org/t/implementing-truncated-backpropagation-through-time/15500/24
아래는 Chat GPT로 구현한 예제이다.
import torch
import torch.nn as nn
# 예제를 위한 RNN 모델 정의
class SimpleRNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(SimpleRNN, self).__init__()
self.hidden_size = hidden_size
self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
batch_size = x.size(0)
hidden = self.init_hidden(batch_size)
out, _ = self.rnn(x, hidden)
out = self.fc(out[:, -1, :]) # 마지막 시퀀스에 대한 결과만 사용
return out
def init_hidden(self, batch_size):
return torch.zeros(1, batch_size, self.hidden_size) # 초기 hidden state 생성
# Truncated BPTT 구현
def train_truncated_bptt(model, criterion, optimizer, data, sequence_length=10, num_epochs=10):
for epoch in range(num_epochs):
for i in range(0, len(data), sequence_length):
# 시퀀스를 작은 조각으로 나눔
inputs = data[i:i+sequence_length]
targets = data[i+sequence_length]
optimizer.zero_grad()
# 순전파
outputs = model(inputs.unsqueeze(0)) # 배치 차원 추가
loss = criterion(outputs, targets.unsqueeze(0)) # 손실 계산
# 역전파 및 최적화
loss.backward()
optimizer.step()
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item()}')
# 예제 데이터 생성
input_size = 1
hidden_size = 16
output_size = 1
data = torch.sin(torch.arange(0, 1000) * 0.1) # 임의의 sine 데이터 생성
# 모델, 손실 함수, 옵티마이저 초기화
model = SimpleRNN(input_size, hidden_size, output_size)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# Truncated BPTT 학습
train_truncated_bptt(model, criterion, optimizer, data)
앞서 언급해온 Long Term Dependency와 관련한 문제를 해결하기 위해 TBPTT를 사용할 수 있지만, RNN대신에 Cell-State를 도입한 LSTM을 사용하여 문제를 해결할 수도 있다.
💡 장기 의존성을 챙기기 위해서 직통 통로를 뚫어 놓았다고 이해하면 좋다.
LSTM의 구조
LSTM은 2가지 Input Gate와 Forget Gate를 이용해서 Cell-State가 이전의 정보를 얼마나 잊고 현재의 정보를 얼마나 기억할지를 정한다.
또 현재의 입력과 Cell-State를 조합하여 새로운 Hidden State를 생성하는 Output Gate를 두게된다.
각각을 살펴보면 다음과 같다.
1) Forget Gate
이전으로부터 받은 정보와 우리가 새로 입력 받은 정보를 토대로 이전의 정보를 얼마나 잊을지 결정하는 Forget Gate
2) Input Gate
우리가 입력 받은 정보를 토대로 우리의 입력 값을 얼마나 입력할지 결정하는 Input gate
3) Cell State
💡 이때, LSTM에서 Sigmoid함수는 값을 얼마나 기억할지, 잊을지를 결정하는 말 그대로 Gate의 역할을 하기 위해서 사용하고 Tanh는 값을 입력할 때 방향까지 고려하기 위해서 사용한다.
4) Output Gate
5) Hidden State
❗ LSTM도 Gradient Vanishing을 완전히 막는 것은 아니다. 단지 Cell State를 도입함으로써 Gradient가 초반 부분까지 잘 전달 되도록 어느 정도 유지해주는 역할을 한다.
→ RNN을 쌓은 것이고, Input Sequence 로부터 정보를 받아서 순전파 시킨다.
→ QA Task에서 Question의 각 단어가 Input 이다.
→ Enocder 부분의 Final Hidden State이다.
→ 모델의 예측 정확도 향상을 위해 Input Element의 정보를 Encapsulate 하는 것이 목표이다.
→ 모델의 Decoder 부분의 최초 Hidden State 역할을 한다.
→ Encoder의 RNN을 쌓은 것이고 각 Time Step에 대해서 예측을 한다.
최근에 다량의 서로 연관된 Time Series를 예측하는 문제가 대두 됐다.
예를 들어 거대한 retailer에 의해 제공되는 모든 상품들에 대한 수요 예측 등이 있다
이러한 예제에서는 유사한 종류의 Time Series가 예측에 영향을 미칠 수 있다.
유사한 종류의 Time Series Data를 사용하는 것은 과적합 없이 모델을 더욱 Flexible하게 Fitting할 수 있고 Neural Network를 사용하면서 Classical Technique 들에서 요구됐던 feature engineering과 model selection 과정을 덜 할 수 있다
DeepAR은 LSTM기반의 모델로써, 확률분포를 추정하는 것을 목표로 한다. 우리는 일상생활에서 다음과 같은 분포를 일반적으로 볼 수 있다.
Sales 예시에서의 분포로써 많이 말리는 물건은 극소수 이고 적게 팔리는 물건이 매우 많다는 것이다.
이 논문의 메인 Contribution은 다음과 같다.
💡 Effectiveness of Deep-learning for Time Series
→ 확률분포를 근사하는데 여러 확률분포를 포함하는 RNN Architecture를 제안한다. 이 모델은 여러 다른 스케일을 가지는 데이터를 효과적으로 다룬다.
→ 여러 Real World Data에 대해 이 모델이 정확함을 보여줌으로써 시계열에 대한 Deep Learning based Approach가 충분히 효과적임을 보여준다.
시점 t에서 시계열 i의 값을 로 하자
우리의 목표는 각 시계열 i에 대해서 conditional distribution( conditional probability function)을 구하는 것이다
다음과 같이 notation을 정의 한다
[Future]
: prediction Range
[Past]
: Conditioning Range
[Covariates]
: 우리는 Covariates를 모든 시간 범위에 대해 알고 있다고 가정한다
우리의 모델은 RNN 구조의 network architecture이고 우리는 모델의 예측 확률 분포를 likelihood factor의 곱으로 가정한다.
likelihood function
log likelihood function
표본 데이터(sample)를 모두 평균 값으로 지정해 likelihood 값을 계산하고 likelihood가 가장 큰 지점을 찾는다. 이렇게 찾게된 지점은 데이터와 제일 잘 맞는 모델과 추정치를 계산할 수 있게 된다.
likelihood function을 세타에 대해 편미분하고 그 값이 0이 되도록하는 세타를 찾는 방식으로 추정을 진행한다.
여기서 는 각 time에 대한 RNN의 출력값이다
여기서 h는 LSTM cell을 갖고 있는 multi-layer RNN(Layer개수가 3개 number of hidden units(hidden unit의 dimension)는 작은 건 40개 큰 것은 120개) 에 의해 구현된 함수이다
Training
Train과 prediction의 Figure는 다음과 같다
이 architecture 뒤에 있는 아이디어는 직관적이다:
여기서의 목표는 each time step의 분포에 대해 예측하는 것이다. 이것은 network 가 이전의 관찰값()을 optional covariates인 와 함께 input으로 받아야 한다는 것을 의미한다.
( 그 시점의 관찰 값을 추정하는 것이기 때문이 한 타임 이전 스텝의 값을 받는다.)
그 정보는 hidden layer로 전파된다. 여기서는 loss function대신 objective function으로 likelihood function을 사용한다 likelihood function은 Gaussian 이거나 Negative Binomial 일 수 있다.
우리는 아래의 Objective Function의 stochastic gradient descenting을 통해 RNN의 h(·)에 구성된 가중치 𝛩뿐만 아니라 위의 (·)의 가중치 또한 갱신할 수 있다.
t = ~ : prediction의 모든값
i = 1~N : 모든 Time Series
우리는 물건 데이터 집합의 각 시계열에 대해 원래 시계열과는 다른 시작점을 선택해서 여러 개의 Training Instance를 생성한다.
이때 전체 길이 T뿐만 아니라 모든 Training Instance에 대해 conditioning range의 길이와 prediction range의 길이를 같게 설정한다
예를 들어, 주어진 시계열의 총 사용 가능 범위가 2013-01-01부터 2017-01-01까지일 때 우리는 training example을 1일 단위로 생성해서 2013-01-01, 2013-01-02, 2013-01-03 이런 식으로 생성할 수 있다.
이때 training window는 우리의 ground-truth-data를 prediction window가 포함하도록 생성 해야 한다.
그러나 우리는 시계열 데이터 시작 전 값인 2012-12-01이 시작점으로 선택 할 수도 있다 이러한 관측되지 않은 target값은 0으로 채운다. 이러한 과정으로 인해 새롭게 나타나는 시계열의 행동에 대해 학습할 수 있도록 한다
💡 “Ground-Truth”는 '우리가 정한 정답’,’우리의 모델이 우리가 원하는 답으로 예측해주길 바라는 답’이다.
By augmenting the data using this windowing procedure, we ensure that information about absolute time is only available to the model through covariates, but not through the relative position of z_i,t in the time series
prediction
우리는 encoding과정에서 마지막으로 얻은 cell state값을 prediction의 초기값으로 설정하고 encoding에서 얻은 마지막 output값을 prediction의 초기 관측 값으로 설정한다
이제 미래를 예측할 시간이다: 유념해야 할 것이 우리가 매 time step에서 얻는 예측은 distribution인 것을 기억해라 우리는 첫번째 timestep에서 output distribution으로부터 하나의 sample 뽑음으로써 시작한다 여기서 뽑은 sample은 두번째 time step에서의 input이 되고 and so on.
이렇게 만들어낸 Objective function을 Backpropagation을 통해 최적화를 진행한다
Problem
우리가 일상에서 쉽게 볼 수 있는 Power-law distribution을 보이는 data에 모델을 적용하는 것은 두가지의 문제점이 있다
첫째로 모델의 autoregressive 속성 때문에 input과 output이 observation 의 scale에 직접적으로 영향을 받는다 따라서 이를 input layer에서 적절히 scale 해주고 output layer에서 다시 inverse 해주는 방법을 사용한다
여기서 그럼 는 무엇이냐?
이는 item-dependent scale factor 로 scale을 하는데 예를 들어 negative binomial likelihood function에는 다음과 같이 scale 해준다
이 논문에서는 를 heuristic하게 선택한 average value로 scale을 해봤는데 모델의 성능이 괜찮았다
두번째로 대부분의 상품이 적게 팔리고 아주 극소수의 상품만이 많이 팔리는 데이터의 불균형 때문에 training instance를 random하게 뽑아버리면 많이 팔리는 극소수의 상품이 뽑힐 확률이 매우 적고 결국 모델이 underfitting 될 가능성이 있다 이는 demand forecasting에서 많이 팔리는 상품을 더 잘 예측하는 것이 business 측면에서 유리하기 때문에 non uniform하게 데이터를 선택해야한다
따라서 위에서 구한 를 이용해서 가 큰 Product가 더 잘 뽑히도록 확률을 크게 조정했다.
이 결과에서 중요한 것은 이전의 데이터가 거의 없고 거의 안 팔리는 것도 어느정도 잘 맞추는 걸 볼 수 있다
모델의 Forecasting Interval을 보면 미래로 갈수록 더 불확실한 것을 Interval의 크기가 커지는 것으로 보여주고 있다.
잘 보고 갑니다!