기본 콘텐츠로 건너뛰기

[ML] 결정트리(Decision Tree) 모델

순환신경망(RNN)

내용

순환신경망(RNN)

RNN

전통적 신경망들은 전부 은닉층에서 활성화 함수를 지난 값은 오직 출력층 방향으로만 향했습니다. 이와 같은 신경망들을 피드 포워드 신경망(Feed Forward Neural Network)이라고 합니다. 그에 반해 RNN(Recurrent Neural Network)은 은닉층의 노드에서 활성화 함수를 통해 나온 결과값을 출력층 방향으로도 보내면서, 다시 은닉층 노드의 결과를 다음 은닉층의 입력으로 보내는 특징을 갖고있습니다.

다음 그림에서 x는 입력층의 입력벡터, y를 출력층의 출력벡터를 나타내며 은닉층에서 활성함수를 통해 결과를 출력층으로 보냅니다. 이 층을 cell(메모리 셀)이라고 합니다. 즉, 현 시점(t)의 cell은 이전 시점(t-1)의 cell로 부터의 출력을 입력으로 사용합니다. 이렇게 은닉상태에서 다른 은닉상태로 전달하는 값을 은닉상태(hidden state)라고 합니다. 결과적으로 다음 그림에서 나타낸 것과 같이 현 시점에는 이전시점으로부터의 은닉상태와 현시점의 입력벡터가 전달됩니다.

위 그림에서 cell은 hidden layer(은닉층)을 나타내며 x1은 은닉층1(cell1)을 통과하여 출력 y1을 생성합니다. 이 생성에 관여되는 매개변수는 cell2에 영향을 주고 이층에 입력인 x2에 대응하여 출력 y2를 반환합니다. 이 과정의 반복으로 최종 출력인 yn이 생성됩니다.

위 과정은 다음 그림과 같이 축약하여 나타낼 수 있습니다.

다음 그림은 두 시점 간의 RNN을 신경망으로 나타낸 것입니다. 4개의 입력벡터와 2개의 은닉상태 그리고 각 시점에서 2개의 출력벡터를 반환합니다. 시점간의 4개의 은닉상태가 존재합니다.

위 과정은 다음과 같이 축약하여 나타낼 수 있습니다. 현재시점 t에서의 은닉상태를 ht라고 하면 이를 계산하기 위해 입력되는 가중치는 이전 셀로부터 전달되는 은닉상태인 ht-1에서의 가중치 wh와 입력벡터(층)으로부터의 가중치 wx입니다. 즉, 현 시점 t에서의 은닉층과 출력층에서의 계산은 다음과 같습니다.

은닉층
ht=tanh(wxxt+whht-1+b)
출력층
yt=f(wyht+b)

입력 벡터의 차원이 d×1이라면 은닉층에 관련된 연산의 각 차원을 나타내보면 다음과 같습니다.

ht=tanh(wxxt+whht-1+b)
ht=tanh((Dh, Dh)×(Dh, 1)×(Dh, d)×(d,1)+(Dh, 1)=(Dh,1)

일반적으로 ht의 계산은 tanh() 함수를 사용하지만 ReLU() 함수를 사용하기도 합니다.

python으로 RNN 구현

ht의 계산을 위한 의사코드(pseudocode)는 다음과 같습니다.

hidden_state_t=0 #최기은닉상태(초기화)
 for input_t in input_length: #시점마다의 입력벡터
     output_t=tanh(input_t, hidden_state_t) 
     # 현시점마다 입력벡터와 이전시점의 은닉상태로 연산
     #출력층에 입력되는 결과 즉, 현시점에서 은닉상태
     hidden_state_t=output_t #은닉상태의 업데이트

위 의사코드에서 input_length는 총 시점의 수가 되며 각 셀(은닉층)은 직전시점의 은닉상태와 현시점의 입력벡터를 비선형함수(활성화 함수)인 tanh()로 연산합니다. 그 결과가 현시점 은닉층의 은닉상태가 됩니다.

위 코드를 구현해봅니다. 여기서는 입력벡터는 2차원으로 즉, (시점들, 입력크기) 차원의 임의 데이터를 사용합니다. 대부분의 경우 입력벡터의 차원은 (batch_size, 시점들, 입력크기)와 같이 3차원입니다.

numpy를 사용하여 모델(layers)를 구성해봅니다.

import numpy as np
timesteps=10
inputSize=4
hiddenSize=8
np.random.seed(0)
inputs=np.random.random((timesteps, inputSize))
hiddenState=np.zeros((hiddenSize,))
print(hiddenState)
[0. 0. 0. 0. 0. 0. 0. 0.]
wx=np.random.random((hiddenSize, inputSize))
wh=np.random.random((hiddenSize, hiddenSize))
b=np.random.random((hiddenSize,))
print(wx.shape, wh.shape, b.shape)
(8, 4) (8, 8) (8,)
totalHiddenState=[]
for i in inputs:
    output=np.tanh(np.dot(wx, i)+np.dot(wh, hiddenState)+b)
    totalHiddenState.append(list(output))
    print(np.shape(totalHiddenState))
    hiddenState=output
(1, 8)
    (2, 8)
    (3, 8)
    (4, 8)
    (5, 8)
    (6, 8)
    (7, 8)
    (8, 8)
    (9, 8)
    (10, 8)
print(totalHiddenState)
[[0.911701180297855, 0.9348851294094611, …, 0.9999817310901117, 0.9999932053003053]]

torch.nn 모듈을 사용하여 모델(layers)를 구성해봅니다.

import torch 
import torch.nn as nn

입력의 크기와 은닉상태의 크기를 정의합니다. 은닉상태의 크기는 RNN의 하이퍼파리미터(hyper-parameter)입니다. 입력텐서는 (배치, 시점, features)의 차원을 가집니다.

batchSize=1
timestep=10
inputSize=5
hiddenSize=8
inputs=torch.Tensor(batchSize, timestep, inputSize)
inputs
 tensor([[[-1.9208e-29,  3.0963e-41,  0.0000e+00,  0.0000e+00,  0.0000e+00],
             ⋮,
             [ 1.3563e-19,  2.7045e-43,  0.0000e+00, -1.9327e-29,  3.0963e-41]]])

nn.RNN()을 사용하여 셀을 구성합니다.

nn.RNN(inputSize, hiddenSize, num_layers=1, batch_first=True, bidirection=False)
cell=nn.RNN(inputSize, hiddenSize, batch_first=True)
output, _status=cell(inputs)
output.shape
torch.Size([1, 10, 8])

위와 같이 구축한 cell에 입력 벡터를 전달하면 두개의 값이 반환됩니다. 첫번째는 모든 은닉상태의 값들이고 두번째는 마지막 은닉상태의 값입니다.

_status.shape
torch.Size([1, 1, 8])

1개 이상의 은닉층들을 구성할 수 있습니다. nn.RNN()의 함수에 num_layers의 매개변수의 전달값으로 결정됩니다. 각 은닉층에서의 연산은 위와 동일하지만 최종 은닉상태의 차원은 은닉층 당 반환되므로 위의 차원과 차이를 보입니다. 즉, 그 차원은 (층의수, 배치크기, 은닉상태의 크기)가 됩니다.

cell=nn.RNN(inputSize, hiddenSize, num_layers=2, batch_first=True)
output, _status=cell(inputs)
output.shape
torch.Size([1, 10, 8])
_status.shape
torch.Size([2, 1, 8])

양방향 RNN

다음 그림과 같이 t 시점을 분석하기 위해 이전시점(t-1)의 결과를 사용하였지만 이후 시점(t+1)의 값들을 적용할 수 있습니다.

위 nn.RNN() 함수에서 매개변수 bidirectional=True로 설정함으로서 실행할 수 있습니다.

cell=nn.RNN(inputSize, hiddenSize, num_layers=2, batch_first=True, bidirectional=True)
output,_status=cell(inputs)
output.shape
torch.Size([1, 10, 16])
_status.shape
torch.Size([4, 1, 8])

주가 데이터에 rnn 층 생성

FinanceDataReader 패키지를 사용하여 특정한 주가 데이터를 호출합니다.

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import torch.nn.functional as F
import FinanceDataReader as fdr
st=pd.Timestamp(2021,8, 26)
et=pd.Timestamp(2022, 3, 25)
kos=fdr.DataReader('KS11', st, et)
kos.head(3)
Close Open High Low Volume Change
Date
2021-08-26 3128.53 3151.30 3159.37 3121.04 727720000.0 -0.0058
2021-08-27 3133.90 3116.29 3143.01 3106.97 520670000.0 0.0017
2021-08-30 3144.19 3160.87 3164.87 3132.77 602860000.0 0.0033

데이터 중 'Close'를 label, 나머지를 feature로 설정합니다. feature와 label 은 하루의 시차를 둡니다.

ind=kos.drop('Close', axis=1)
de=kos['Close']

호출한 데이터의 형식은 pandas의 DataFrame 입니다. 이를 numpy 배열 형식으로 전환하기 위해 .values 속성을 사용합니다.

x=ind.values[:-1, :]
y=de.values[1:].reshape(-1,1)

features의 표준화를 실시합니다. sklearn.preprocessing.StandardScaler()클래스를 사용합니다.

xScaler=StandardScaler().fit(x)
xN=xScaler.transform(x)
xN[:3]
array([[ 1.42790852,  1.39979448,  1.36045449,  0.77341112, -0.45492534],
[ 1.20354701, 1.29394449, 1.27088467, -0.69620736, 0.24960505], [ 1.48923784, 1.43537974, 1.43512783, -0.11283164, 0.39990487]])

데이터를 torch.float32 형으로 전환합니다.

xtensor=torch.tensor(xN, dtype=torch.float)
xtensor.dtype, xtensor.shape
(torch.float32, torch.Size([140, 5]))

현재 데이터는 (sample, feature)의 차원을 가집니다. 이 차원을 (batch, sample, feature)로 변경하기 위해 .view() 메소드를 적용합니다.

xtensor1=xtensor.view(1, 140,5)
xtensor1.shape
torch.Size([1, 140, 5])
batchSize, timestemp, inputSize=xtensor1.shape
hiddenSize=8
layerN=2
cell=nn.RNN(inputSize, hiddenSize, num_layers=2, batch_first=True)
_status,output=cell(xtensor1)
output.shape, _status.shape
(torch.Size([2, 1, 8]), torch.Size([1, 140, 8]))

다음은 RNN 층에서 최종 출력되는 은닉상태의 입니다.

output
tensor([[[ 0.2074,  0.0375, -0.1976, -0.6594, -0.7519,  0.9465, -0.1608,
0.3195]], [[ 0.5834, -0.2388, -0.5469, 0.1141, 0.0517, 0.8232, -0.0088, -0.0515]]], grad_fn=)

댓글

이 블로그의 인기 게시물

[Linear Algebra] 유사변환(Similarity transformation)

유사변환(Similarity transformation) n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사행렬(similarity matrix)이 되며 행렬 A를 가역행렬 P와 B로 분해하는 것을 유사 변환(similarity transformation) 이라고 합니다. $$\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B $$ 식 2는 식 1의 양변에 B의 고유값을 고려한 것입니다. \begin{align}\tag{식 2} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align} 식 2의 행렬식은 식 3과 같이 정리됩니다. \begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \textsf{det}(I)\end{aligned}\end{align} 유사행렬의 특성 유사행렬인 두 정방행렬 A와 B는 'A ~ B' 와 같

[matplotlib] 히스토그램(Histogram)

히스토그램(Histogram) 히스토그램은 확률분포의 그래픽적인 표현이며 막대그래프의 종류입니다. 이 그래프가 확률분포와 관계가 있으므로 통계적 요소를 나타내기 위해 많이 사용됩니다. plt.hist(X, bins=10)함수를 사용합니다. x=np.random.randn(1000) plt.hist(x, 10) plt.show() 위 그래프의 y축은 각 구간에 해당하는 갯수이다. 빈도수 대신 확률밀도를 나타내기 위해서는 위 함수의 매개변수 normed=True로 조정하여 나타낼 수 있다. 또한 매개변수 bins의 인수를 숫자로 전달할 수 있지만 리스트 객체로 지정할 수 있다. 막대그래프의 경우와 마찬가지로 각 막대의 폭은 매개변수 width에 의해 조정된다. y=np.linspace(min(x)-1, max(x)+1, 10) y array([-4.48810153, -3.54351935, -2.59893717, -1.65435499, -0.70977282, 0.23480936, 1.17939154, 2.12397372, 3.0685559 , 4.01313807]) plt.hist(x, y, normed=True) plt.show()

R 미분과 적분

내용 expression 미분 2차 미분 mosaic를 사용한 미분 적분 미분과 적분 R에서의 미분과 적분 함수는 expression()함수에 의해 생성된 표현식을 대상으로 합니다. expression expression(문자, 또는 식) 이 표현식의 평가는 eval() 함수에 의해 실행됩니다. > ex1<-expression(1+0:9) > ex1 expression(1 + 0:9) > eval(ex1) [1] 1 2 3 4 5 6 7 8 9 10 > ex2<-expression(u, 2, u+0:9) > ex2 expression(u, 2, u + 0:9) > ex2[1] expression(u) > ex2[2] expression(2) > ex2[3] expression(u + 0:9) > u<-0.9 > eval(ex2[3]) [1] 0.9 1.9 2.9 3.9 4.9 5.9 6.9 7.9 8.9 9.9 미분 D(표현식, 미분 변수) 함수로 미분을 실행합니다. 이 함수의 표현식은 expression() 함수로 생성된 객체이며 미분 변수는 다음 식의 분모의 변수를 의미합니다. $$\frac{d}{d \text{변수}}\text{표현식}$$ 이 함수는 어떤 함수의 미분의 결과를 표현식으로 반환합니다. > D(expression(2*x^3), "x") 2 * (3 * x^2) > eq<-expression(log(x)) > eq expression(log(x)) > D(eq, "x") 1/x > eq2<-expression(a/(1+b*exp(-d*x))); eq2 expression(a/(1 + b * exp(-d * x))) > D(eq2, "x") a * (b * (exp(-d * x) * d))/(1 + b