AI를 이용한 주식가격 예측에 관심이 많습니다. 인터넷에서 가져온 내용을 한국사람이 읽기 좋게 정리하여 공유합니다.
통계학적 용어와, AI분석용어가 섞여서 읽어내기 쉽지 않지만, 보다 정확한 주식예측을 위해서 다양한 접근방법을 사용하는 것을 보면서 지혜를 얻으시면 좋겠습니다.
이 노트북에서 나는 주가 움직임을 예측하기 위한 완전한 프로세스를 만들 것입니다. 잘 따라오시면 꽤 좋은 결과를 얻을 수 있을 것입니다. 이를 위해 Recurrent Neural Network의 일종인 LSTM 을 Generator로 하고 Convolutional Neural Network인 CNN 을 Discriminator로 사용하는 GAN( Generative Adversarial Network )을 사용합니다. 우리는 시계열 데이터를 예측하려는 명백한 이유로 LSTM을 사용합니다. GAN, 특히 CNN을 판별자로 사용합니다.
가장 어려운 부분은 GAN입니다. GAN을 성공적으로 교육하는 데 매우 까다로운 부분은 올바른 하이퍼파라미터 세트를 얻는 것입니다. 이러한 이유로 우리는 GAN의 하이퍼파라미터(탐색 대 착취 딜레마)를 변경하는 시기와 방법을 결정하기 위해 베이지안 최적화 (가우시안 프로세스와 함께) 및 강화 학습 (RL)을 사용할 것입니다. 강화 학습을 만들 때 Rainbow 및 PPO 와 같은 현장에서 가장 최근의 발전을 사용할 것입니다 .
다양한 유형의 입력 데이터를 사용할 것입니다. 주식의 과거 거래 데이터 및 기술 지표와 함께 NLP 의 최신 기술 ('Bidirectional Embedding Representations from Transformers', BERT , 일종의 NLP 전이 학습 사용)을 사용하여 감정 분석(기본 분석의 소스로 사용)을 생성합니다. ), 전체 추세 방향 추출을 위한 푸리에 변환 , 다른 상위 수준 기능을 식별하기 위한 누적 자동 인코더 , 상관 자산 찾기를 위한 고유 포트폴리오 , 자동 회귀 통합 이동 평균( ARIMA) 주식에 대한 가능한 한 많은 정보, 패턴, 종속성 등을 캡처하기 위해 주식 함수 근사화 등을 위해. 우리 모두 알다시피 (데이터)가 많을수록 더 즐겁습니다. 주가 움직임을 예측하는 것은 매우 복잡한 작업이므로 다양한 관점에서 주식에 대해 더 많이 알수록 변화가 더 커집니다.
모든 신경망을 생성하기 위해 MXNet과 그 고급 API인 Gluon을 사용하고 여러 GPU에서 훈련합니다.
참고: 거의 모든 알고리즘과 기술의 배후에 있는 수학 및 메커니즘에 대해 자세히 알아보려고 노력했지만 이 노트북은 머신/딥 러닝 또는 주식 시장이 작동하는 방식을 명시적으로 설명하기 위한 것이 아닙니다. 목적은 오히려 주가 움직임을 정확하게 예측하기 위해 다양한 기술과 알고리즘을 사용할 수 있는 방법을 보여주고 각 단계에서 각 기술을 사용하는 이유와 유용성에 대한 근거를 제공하는 것입니다.
그림 1 - 작업의 전체 아키텍처
특정 주식이 특정 방향으로 움직이기 위해서는 수백만 개의 이벤트와 전제 조건이 있기 때문에 주식 시장을 정확하게 예측하는 것은 복잡한 작업입니다. 따라서 이러한 전제 조건을 최대한 많이 포착할 수 있어야 합니다. 우리는 또한 몇 가지 중요한 가정을 해야 합니다. 1) 시장은 100% 무작위가 아니며, 2) 역사는 반복되고, 3) 시장은 사람들의 합리적인 행동을 따르고, 4) 시장은 ' 완벽 '합니다.
Goldman Sachs (NYSE: GS) 의 가격 변동을 예측해 보겠습니다 . 이를 위해 2010년 1월 1일부터 2018년 12월 31일까지 일일 종가를 사용합니다(교육 목적으로 7년, 검증 목적으로 2년). 우리는 'Goldman Sachs'와 'GS'라는 용어를 같은 의미로 사용할 것 입니다.
GS(골드만삭스)의 주가가 오르느냐 내리느냐에 어떤 영향을 미치는지 이해해야 합니다. 그것은 사람들이 전체적으로 생각하는 것입니다. 따라서 가능한 한 많은 정보(다양한 측면과 각도에서 주식을 묘사)를 통합해야 합니다. (일별 데이터 - 1,585일을 사용하여 다양한 알고리즘을 훈련하고(보유한 데이터의 70%) 다음 680일(테스트 데이터)을 예측합니다. 그런 다음 예측된 결과를 테스트(홀드아웃) 데이터와 비교합니다. . 데이터의 각 유형( 기능 이라고 함)은 이후 섹션에서 자세히 설명하지만 상위 수준 개요로 사용할 기능은 다음과 같습니다.
1) 10-K, 10-Q 리포트를 이용한 회사 실적 분석, ROE, P/E 등 분석(사용하지 않음),
2) 뉴스 등 두 가지 기능이 있습니다.-
잠재적인 뉴스는 특정 방향으로 주식을 잠재적으로 움직일 수 있는 다가오는 이벤트를 나타낼 수 있습니다. Goldman Sachs에 대한 모든 일일 뉴스를 읽고 그날의 Goldman Sachs에 대한 전체 감정이 긍정적인지, 중립적인지 또는 부정적인지(0에서 1까지의 점수로) 추출합니다. 많은 투자자들이 뉴스를 면밀히 읽고 (물론 부분적으로) 뉴스를 기반으로 투자 결정을 내리기 때문에 오늘 Goldman Sachs에 대한 뉴스가 매우 긍정적이라면 내일 주식이 급등할 가능성이 다소 높습니다. 한 가지 중요한 점은 나중에 절대적으로 모든 기능(이 기능 포함)에 대해 기능 중요도(GS의 움직임을 나타내는 정도)를 수행하고 사용할지 여부를 결정할 것입니다.
정확한 감정 예측을 생성하기 위해 신경 언어 처리( NLP )를 사용합니다. 감정 분류 스톡 뉴스 감정 추출을 위한 전이 학습을 위해 Google에서 최근에 발표한 NLP 접근 방식인 BERT 를 사용 합니다.
다음으로 기능이 너무 많기 때문에 몇 가지 중요한 단계를 수행해야 합니다.
데이터 준비의 마지막 단계로 자동 인코더에서 생성된 기능의 차원을 줄이기 위해 주성분 분석( PCA )을 사용하여 고유 포트폴리오 를 생성합니다.
from utils import *
import time
import numpy as np
from mxnet import nd, autograd, gluon
from mxnet.gluon import nn, rnn
import mxnet as mx
import datetime
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.decomposition import PCA
import math
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import StandardScaler
import xgboost as xgb
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings("ignore")
context = mx.cpu(); model_ctx=mx.cpu()
mx.random.seed(1719)
참고 : 이 섹션(3. 데이터)의 목적은 데이터 전처리를 보여주고 다양한 데이터 소스를 사용하는 근거를 제시하는 것이므로 전체 데이터의 하위 집합(훈련에 사용됨)만 사용하겠습니다.
def parser(x):
return datetime.datetime.strptime(x,'%Y-%m-%d')
dataset_ex_df = pd.read_csv('data/panel_data_close.csv', header=0, parse_dates=[0], date_parser=parser)
dataset_ex_df[['Date', 'GS']].head(3)
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
2009-12-31 | 168.839996 |
2010-01-04 | 173.080002 |
2010-01-05 | 176.139999 |
print('There are {} number of days in the dataset.'.format(dataset_ex_df.shape[0]))
There are 2265 number of days in the dataset.
지난 9년간의 주식을 시각화해 봅시다. 파선 세로선은 훈련 데이터와 테스트 데이터 사이의 구분을 나타냅니다.
plt.figure(figsize=(14, 5), dpi=100)
plt.plot(dataset_ex_df['Date'], dataset_ex_df['GS'], label='Goldman Sachs stock')
plt.vlines(datetime.date(2016,4, 20), 0, 270, linestyles='--', colors='gray', label='Train/Test data cut-off')
plt.xlabel('Date')
plt.ylabel('USD')
plt.title('Figure 2: Goldman Sachs stock price')
plt.legend()
plt.show()
num_training_days = int(dataset_ex_df.shape[0]*.7)
print('Number of training days: {}. Number of test days: {}.'.format(num_training_days, \
dataset_ex_df.shape[0]-num_training_days))
Number of training days: 1585. Number of test days: 680.
앞에서 설명한 것처럼 GS뿐만 아니라 다른 자산을 기능으로 사용할 것입니다.
그렇다면 다른 어떤 자산이 GS의 주식 움직임에 영향을 미칠까요? 회사, 비즈니스 라인, 경쟁 구도, 종속 관계, 공급업체 및 클라이언트 유형 등에 대한 올바른 이해는 올바른 관련 자산 집합을 선택하는 데 매우 중요합니다.
우리는 기술 지표가 무엇이고 왜 사용하는지 이미 다루었으므로 바로 코드로 넘어가겠습니다. GS만을 위한 기술적 지표를 만들어 드립니다.
def get_technical_indicators(dataset):
# Create 7 and 21 days Moving Average
dataset['ma7'] = dataset['price'].rolling(window=7).mean()
dataset['ma21'] = dataset['price'].rolling(window=21).mean()
# Create MACD
dataset['26ema'] = pd.ewma(dataset['price'], span=26)
dataset['12ema'] = pd.ewma(dataset['price'], span=12)
dataset['MACD'] = (dataset['12ema']-dataset['26ema'])
# Create Bollinger Bands
dataset['20sd'] = pd.stats.moments.rolling_std(dataset['price'],20)
dataset['upper_band'] = dataset['ma21'] + (dataset['20sd']*2)
dataset['lower_band'] = dataset['ma21'] - (dataset['20sd']*2)
# Create Exponential moving average
dataset['ema'] = dataset['price'].ewm(com=0.5).mean()
# Create Momentum
dataset['momentum'] = dataset['price']-1
return dataset
dataset_TI_df = get_technical_indicators(dataset_ex_df[['GS']])
dataset_TI_df.head()
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
2010-02-01 | 153.130005 | 152.374285 | 164.220476 | 160.321839 | 156.655072 | -3.666767 | 9.607375 | 183.435226 | 145.005726 | 152.113609 | 152.130005 | 5.024735 |
2010-02-02 | 156.940002 | 152.777143 | 163.653809 | 160.014868 | 156.700048 | -3.314821 | 9.480630 | 182.615070 | 144.692549 | 155.331205 | 155.940002 | 5.049471 |
2010-02-03 | 157.229996 | 153.098572 | 162.899047 | 159.766235 | 156.783365 | -2.982871 | 9.053702 | 181.006450 | 144.791644 | 156.597065 | 156.229996 | 5.051329 |
2010-02-04 | 150.679993 | 153.069999 | 161.686666 | 158.967168 | 155.827031 | -3.140137 | 8.940246 | 179.567157 | 143.806174 | 152.652350 | 149.679993 | 5.008500 |
2010-02-05 | 154.160004 | 153.449999 | 160.729523 | 158.550196 | 155.566566 | -2.983631 | 8.151912 | 177.033348 | 144.425699 | 153.657453 | 153.160004 | 5.031483 |
그래서 우리는 모든 거래일에 대한 기술적 지표(MACD, 볼린저 밴드 등 포함)를 가지고 있습니다. 총 12개의 기술 지표가 있습니다.
이러한 지표의 지난 400일을 시각화해 보겠습니다.
def plot_technical_indicators(dataset, last_days):
plt.figure(figsize=(16, 10), dpi=100)
shape_0 = dataset.shape[0]
xmacd_ = shape_0-last_days
dataset = dataset.iloc[-last_days:, :]
x_ = range(3, dataset.shape[0])
x_ =list(dataset.index)
# Plot first subplot
plt.subplot(2, 1, 1)
plt.plot(dataset['ma7'],label='MA 7', color='g',linestyle='--')
plt.plot(dataset['price'],label='Closing Price', color='b')
plt.plot(dataset['ma21'],label='MA 21', color='r',linestyle='--')
plt.plot(dataset['upper_band'],label='Upper Band', color='c')
plt.plot(dataset['lower_band'],label='Lower Band', color='c')
plt.fill_between(x_, dataset['lower_band'], dataset['upper_band'], alpha=0.35)
plt.title('Technical indicators for Goldman Sachs - last {} days.'.format(last_days))
plt.ylabel('USD')
plt.legend()
# Plot second subplot
plt.subplot(2, 1, 2)
plt.title('MACD')
plt.plot(dataset['MACD'],label='MACD', linestyle='-.')
plt.hlines(15, xmacd_, shape_0, colors='g', linestyles='--')
plt.hlines(-15, xmacd_, shape_0, colors='g', linestyles='--')
plt.plot(dataset['log_momentum'],label='Momentum', color='b',linestyle='-')
plt.legend()
plt.show()
plot_technical_indicators(dataset_TI_df, 400)
근본적인 분석을 위해 GS에 대한 모든 일일 뉴스에 대한 감정 분석을 수행합니다. 마지막에 시그모이드를 사용하면 결과는 0과 1 사이가 됩니다. 점수가 0에 가까울수록 뉴스가 더 부정적입니다(1에 가까울수록 긍정적인 감정을 나타냄). 매일 평균 일일 점수(0과 1 사이의 숫자)를 생성하고 기능으로 추가합니다.
뉴스를 긍정 또는 부정(또는 중립)으로 분류하기 위해 사전 훈련된 언어 표현인 BERT 를 사용합니다.
사전 훈련된 BERT 모델은 이미 MXNet/Gluon에서 사용할 수 있습니다. 인스턴스화하고 두 개의 (임의의 숫자) Dense레이어를 추가하고 softmax로 이동하기만 하면 됩니다. 점수는 0에서 1까지입니다.
# just import bert
import bert
BERT 및 NLP 부분의 세부 사항으로 들어가는 것은 이 노트북의 범위에 있지 않습니다.
언어 처리와 관련하여 확실히 유망한 BERT 전용 새 저장소를 만들 것입니다..
푸리에 변환 은 함수를 사용하여 일련의 사인파(진폭 및 프레임이 다름)를 생성합니다. 결합되면 이러한 사인파는 원래 함수에 근접합니다. 수학적으로 말하면 변환은 다음과 같습니다.
G(에프)=∫-∞∞g(티)이자형-나2파이에프티디티
푸리에 변환을 사용하여 골드만삭스 주식의 글로벌 및 로컬 추세를 추출하고 노이즈를 약간 제거합니다. 어떻게 작동하는지 봅시다.
data_FT = dataset_ex_df[['Date', 'GS']]
close_fft = np.fft.fft(np.asarray(data_FT['GS'].tolist()))
fft_df = pd.DataFrame({'fft':close_fft})
fft_df['absolute'] = fft_df['fft'].apply(lambda x: np.abs(x))
fft_df['angle'] = fft_df['fft'].apply(lambda x: np.angle(x))
plt.figure(figsize=(14, 7), dpi=100)
fft_list = np.asarray(fft_df['fft'].tolist())
for num_ in [3, 6, 9, 100]:
fft_list_m10= np.copy(fft_list); fft_list_m10[num_:-num_]=0
plt.plot(np.fft.ifft(fft_list_m10), label='Fourier transform with {} components'.format(num_))
plt.plot(data_FT['GS'], label='Real')
plt.xlabel('Days')
plt.ylabel('USD')
plt.title('Figure 3: Goldman Sachs (close) stock prices & Fourier transforms')
plt.legend()
plt.show()
그림 3에서 볼 수 있듯이 우리가 사용하는 푸리에 변환의 구성 요소가 많을수록 근사 함수가 실제 주가에 더 가까워집니다(100개 구성 요소 변환은 원래 함수와 거의 동일합니다. 빨간색과 보라색 선이 거의 겹칩니다). 우리는 장단기 추세를 추출하기 위해 푸리에 변환을 사용하므로 3, 6 및 9 구성 요소로 변환을 사용합니다. 3개의 구성 요소가 있는 변환이 장기 추세 역할을 한다고 추론할 수 있습니다.
데이터를 노이즈 제거하는 데 사용되는 또 다른 기술은 웨이블릿 호출 입니다. 웨이블릿과 푸리에 변환은 유사한 결과를 나타내므로 푸리에 변환만 사용합니다.
from collections import deque
items = deque(np.asarray(fft_df['absolute'].tolist()))
items.rotate(int(np.floor(len(fft_df)/2)))
plt.figure(figsize=(10, 7), dpi=80)
plt.stem(items)
plt.title('Figure 4: Components of Fourier transforms')
plt.show()
ARIMA 는 시계열 데이터를 예측하는 기술입니다., ARIMA가 최종 예측으로 사용되지는 않을 것이지만, 주식을 약간 제거하고 (아마도) 몇 가지 새로운 패턴이나 특징을 추출하는 기술로 사용할 것입니다.
from statsmodels.tsa.arima_model import ARIMA
from pandas import DataFrame
from pandas import datetime
series = data_FT['GS']
model = ARIMA(series, order=(5, 1, 0))
model_fit = model.fit(disp=0)
print(model_fit.summary())
ARIMA Model Results
==============================================================================
Dep. Variable: D.GS No. Observations: 2264
Model: ARIMA(5, 1, 0) Log Likelihood -5465.888
Method: css-mle S.D. of innovations 2.706
Date: Wed, 09 Jan 2019 AIC 10945.777
Time: 10:28:07 BIC 10985.851
Sample: 1 HQIC 10960.399
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
const -0.0011 0.054 -0.020 0.984 -0.106 0.104
ar.L1.D.GS -0.0205 0.021 -0.974 0.330 -0.062 0.021
ar.L2.D.GS 0.0140 0.021 0.665 0.506 -0.027 0.055
ar.L3.D.GS -0.0030 0.021 -0.141 0.888 -0.044 0.038
ar.L4.D.GS 0.0026 0.021 0.122 0.903 -0.039 0.044
ar.L5.D.GS -0.0522 0.021 -2.479 0.013 -0.093 -0.011
Roots
=============================================================================
Real Imaginary Modulus Frequency
-----------------------------------------------------------------------------
AR.1 -1.7595 -0.0000j 1.7595 -0.5000
AR.2 -0.5700 -1.7248j 1.8165 -0.3008
AR.3 -0.5700 +1.7248j 1.8165 0.3008
AR.4 1.4743 -1.0616j 1.8168 -0.0993
AR.5 1.4743 +1.0616j 1.8168 0.0993
-----------------------------------------------------------------------------
from pandas.tools.plotting import autocorrelation_plot
autocorrelation_plot(series)
plt.figure(figsize=(10, 7), dpi=80)
plt.show()
<Figure size 800x560 with 0 Axes>
from pandas import read_csv
from pandas import datetime
from statsmodels.tsa.arima_model import ARIMA
from sklearn.metrics import mean_squared_error
X = series.values
size = int(len(X) * 0.66)
train, test = X[0:size], X[size:len(X)]
history = [x for x in train]
predictions = list()
for t in range(len(test)):
model = ARIMA(history, order=(5,1,0))
model_fit = model.fit(disp=0)
output = model_fit.forecast()
yhat = output[0]
predictions.append(yhat)
obs = test[t]
history.append(obs)
error = mean_squared_error(test, predictions)
print('Test MSE: %.3f' % error)
Test MSE: 10.151
# Plot the predicted (from ARIMA) and real prices
plt.figure(figsize=(12, 6), dpi=100)
plt.plot(test, label='Real')
plt.plot(predictions, color='red', label='Predicted')
plt.xlabel('Days')
plt.ylabel('USD')
plt.title('Figure 5: ARIMA model on GS stock')
plt.legend()
plt.show()
그림 5에서 볼 수 있듯이 ARIMA는 실제 주가에 대한 아주 좋은 근사치를 제공합니다. 앞에서 언급한 바와 같이 가능한 한 Goldman Sachs에 대한 많은 기능과 패턴을 캡처하기를 원하기 때문에 ARIMA를 통해 예측된 가격을 LSTM의 입력 기능으로 사용할 것입니다. 우리는 10.151의 MSE(평균 제곱 오차)를 테스트합니다. 그 자체로는 나쁜 결과가 아니지만(테스트 데이터가 많다는 점을 고려하면) 여전히 LSTM의 기능으로만 사용할 것입니다.
데이터의 품질이 좋은지 확인하는 것은 외부 모델에 매우 중요합니다. 데이터가 적합한지 확인하기 위해 기본 데이터 분포에 근본적인 오류가 있다는 사실로 인해 손상되지 않고 달성하고 관찰한 결과가 실제로 실제인지 확인하기 위해 몇 가지 간단한 검사를 수행합니다.
코드가 간단하고 심층 학습 부분에 더 중점을 두지만 데이터는 정성적이므로 여기서는 코드를 다루지 않을 것 입니다.
print('Total dataset has {} samples, and {} features.'.format(dataset_total_df.shape[0], \
dataset_total_df.shape[1]))
Total dataset has 2265 samples, and 112 features.
따라서 모든 유형의 데이터(상관 자산, 기술 지표, 기본 분석, 푸리에 및 아리마)를 추가한 후 2,265일 동안 총 112개의 기능이 있습니다(앞서 언급했듯이 1,585일만 훈련 데이터용임). .
또한 오토인코더에서 생성된 몇 가지 기능이 추가될 것입니다.
너무 많은 기능을 가지고 있기 때문에 우리는 그것들 모두가 GS 주식이 취할 방향을 실제로 나타내는지 여부를 고려해야 합니다. 예를 들어, 우리는 LIBOR의 변화가 경제의 변화를 나타낼 수 있고, 이는 다시 GS 주식 행동의 변화를 나타낼 수 있다고 생각하기 때문에 데이터 세트에 USD 표시 LIBOR 비율을 포함했습니다. 하지만 우리는 테스트가 필요합니다. 기능 중요도를 테스트하는 방법에는 여러 가지가 있지만 우리가 적용할 방법은 분류 및 회귀 문제 모두에서 최상의 결과를 제공하는 XGBoost를 사용하는 것입니다.
기능 데이터 세트가 상당히 크기 때문에 여기에서는 프레젠테이션을 위해 기술 지표만 사용합니다. 실제 기능 중요도 테스트 중에 선택된 모든 기능이 다소 중요한 것으로 판명되었으므로 GAN을 교육할 때 어떤 것도 제외하지 않을 것입니다.
def get_feature_importance_data(data_income):
data = data_income.copy()
y = data['price']
X = data.iloc[:, 1:]
train_samples = int(X.shape[0] * 0.65)
X_train = X.iloc[:train_samples]
X_test = X.iloc[train_samples:]
y_train = y.iloc[:train_samples]
y_test = y.iloc[train_samples:]
return (X_train, y_train), (X_test, y_test)
# Get training and test data
(X_train_FI, y_train_FI), (X_test_FI, y_test_FI) = get_feature_importance_data(dataset_TI_df)
regressor = xgb.XGBRegressor(gamma=0.0,n_estimators=150,base_score=0.7,colsample_bytree=1,learning_rate=0.05)
xgbModel = regressor.fit(X_train_FI,y_train_FI, \
eval_set = [(X_train_FI, y_train_FI), (X_test_FI, y_test_FI)], \
verbose=False)
eval_result = regressor.evals_result()
training_rounds = range(len(eval_result['validation_0']['rmse']))
교육을 관찰하고 과적합이 있는지 확인하기 위해 교육 및 검증 오류를 플로팅해 보겠습니다(과적합이 없음).
plt.scatter(x=training_rounds,y=eval_result['validation_0']['rmse'],label='Training Error')
plt.scatter(x=training_rounds,y=eval_result['validation_1']['rmse'],label='Validation Error')
plt.xlabel('Iterations')
plt.ylabel('RMSE')
plt.title('Training Vs Validation Error')
plt.legend()
plt.show()
fig = plt.figure(figsize=(8,8))
plt.xticks(rotation='vertical')
plt.bar([i for i in range(len(xgbModel.feature_importances_))], xgbModel.feature_importances_.tolist(), tick_label=X_test_FI.columns)
plt.title('Figure 6: Feature importance of the technical indicators.')
plt.show()
MA7, MACD 및 BB가 중요한 기능 중 하나라는 것은 놀라운 일이 아닙니다(주식 거래 경험이 있는 사람들에게는).
전체 데이터 세트에 대해 기능 중요도를 수행하기 위해 동일한 논리를 따랐습니다. 소수의 기능과 비교할 때 교육 시간이 더 오래 걸리고 결과를 읽기가 조금 더 어려웠습니다.
오토인코더로 진행하기 전에 대체 활성화 기능을 살펴보겠습니다.
GELU -Gaussian Error Linear Unites가 최근 제안 되었습니다 . 이 논문에서 저자는 GELU를 사용하는 신경망이 활성화로 ReLU를 사용하는 신경망을 능가하는 몇 가지 사례를 보여줍니다. 뉴스 정서 분석에 사용한 NLP 접근 방식인 BERTgelu 에서도 사용됩니다 .
오토인코더에는 GELU를 사용합니다.
참고 : 아래 셀은 GELU 수학의 논리를 보여줍니다. 활성화 함수로 실제 구현된 것은 아닙니다. MXNet 내부에 GELU를 구현해야 했습니다. 코드를 따르고 변경 act_type='relu'하면 act_type='gelu'MXNet 구현을 변경하지 않는 한 작동하지 않습니다. GELU의 MXNet 구현에 액세스하려면 전체 프로젝트에서 풀 요청을 만드십시오.
def gelu(x):
return 0.5 * x * (1 + math.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * math.pow(x, 3))))
def relu(x):
return max(x, 0)
def lrelu(x):
return max(0.01*x, x)
GELU, ReLU및를 시각화해 보겠습니다 LeakyReLU(마지막 것은 주로 GAN에서 사용되며 우리도 사용함).
plt.figure(figsize=(15, 5))
plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=.5, hspace=None)
ranges_ = (-10, 3, .25)
plt.subplot(1, 2, 1)
plt.plot([i for i in np.arange(*ranges_)], [relu(i) for i in np.arange(*ranges_)], label='ReLU', marker='.')
plt.plot([i for i in np.arange(*ranges_)], [gelu(i) for i in np.arange(*ranges_)], label='GELU')
plt.hlines(0, -10, 3, colors='gray', linestyles='--', label='0')
plt.title('Figure 7: GELU as an activation function for autoencoders')
plt.ylabel('f(x) for GELU and ReLU')
plt.xlabel('x')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot([i for i in np.arange(*ranges_)], [lrelu(i) for i in np.arange(*ranges_)], label='Leaky ReLU')
plt.hlines(0, -10, 3, colors='gray', linestyles='--', label='0')
plt.ylabel('f(x) for Leaky ReLU')
plt.xlabel('x')
plt.title('Figure 8: LeakyReLU')
plt.legend()
plt.show()
참고 : 이 노트북의 향후 버전에서는 U-Net ( 링크 )을 사용하여 실험하고 컨볼루션 계층을 활용하고 주식의 기본 이동 패턴에 대해 더 많은 기능을 추출(및 생성)하려고 합니다. Dense지금은 레이어 로만 만든 간단한 오토인코더를 사용하겠습니다 .
참고 : 이후 버전에서 살펴볼 한 가지는 디코더의 마지막 레이어를 제거하는 것입니다. 일반적으로 자동 인코더에서 인코더 수 == 디코더 수입니다. 그러나 우리는 디코더의 마지막 레이어를 건너뛸 수 있도록 (동일한 입력을 생성하는 대신) 더 높은 수준의 기능을 추출하기를 원합니다. 교육 중에 동일한 수의 레이어로 인코더와 디코더를 생성하여 이를 달성하지만, 출력을 생성할 때 더 높은 수준의 기능을 포함할 것이므로 유일한 레이어 옆에 있는 레이어를 사용합니다.
batch_size = 64
n_batches = VAE_data.shape[0]/batch_size
VAE_data = VAE_data.values
train_iter = mx.io.NDArrayIter(data={'data': VAE_data[:num_training_days,:-1]}, \
label={'label': VAE_data[:num_training_days, -1]}, batch_size = batch_size)
test_iter = mx.io.NDArrayIter(data={'data': VAE_data[num_training_days:,:-1]}, \
label={'label': VAE_data[num_training_days:,-1]}, batch_size = batch_size)
model_ctx = mx.cpu()
class VAE(gluon.HybridBlock):
def __init__(self, n_hidden=400, n_latent=2, n_layers=1, n_output=784, \
batch_size=100, act_type='relu', **kwargs):
self.soft_zero = 1e-10
self.n_latent = n_latent
self.batch_size = batch_size
self.output = None
self.mu = None
super(VAE, self).__init__(**kwargs)
with self.name_scope():
self.encoder = nn.HybridSequential(prefix='encoder')
for i in range(n_layers):
self.encoder.add(nn.Dense(n_hidden, activation=act_type))
self.encoder.add(nn.Dense(n_latent*2, activation=None))
self.decoder = nn.HybridSequential(prefix='decoder')
for i in range(n_layers):
self.decoder.add(nn.Dense(n_hidden, activation=act_type))
self.decoder.add(nn.Dense(n_output, activation='sigmoid'))
def hybrid_forward(self, F, x):
h = self.encoder(x)
#print(h)
mu_lv = F.split(h, axis=1, num_outputs=2)
mu = mu_lv[0]
lv = mu_lv[1]
self.mu = mu
eps = F.random_normal(loc=0, scale=1, shape=(self.batch_size, self.n_latent), ctx=model_ctx)
z = mu + F.exp(0.5*lv)*eps
y = self.decoder(z)
self.output = y
KL = 0.5*F.sum(1+lv-mu*mu-F.exp(lv),axis=1)
logloss = F.sum(x*F.log(y+self.soft_zero)+ (1-x)*F.log(1-y+self.soft_zero), axis=1)
loss = -logloss-KL
return loss
n_hidden=400 # neurons in each layer
n_latent=2
n_layers=3 # num of dense layers in encoder and decoder respectively
n_output=VAE_data.shape[1]-1
net = VAE(n_hidden=n_hidden, n_latent=n_latent, n_layers=n_layers, n_output=n_output, batch_size=batch_size, act_type='gelu')
net.collect_params().initialize(mx.init.Xavier(), ctx=mx.cpu())
net.hybridize()
trainer = gluon.Trainer(net.collect_params(), 'adam', {'learning_rate': .01})
print(net)
VAE(
(encoder): HybridSequential(
(0): Dense(None -> 400, Activation(relu))
(1): Dense(None -> 400, Activation(relu))
(2): Dense(None -> 400, Activation(relu))
(3): Dense(None -> 4, linear)
)
(decoder): HybridSequential(
(0): Dense(None -> 400, Activation(relu))
(1): Dense(None -> 400, Activation(relu))
(2): Dense(None -> 400, Activation(relu))
(3): Dense(None -> 11, Activation(sigmoid))
)
)
따라서 인코더와 디코더 모두에 3개의 레이어(각각 400개의 뉴런 포함)가 있습니다.
n_epoch = 150
print_period = n_epoch // 10
start = time.time()
training_loss = []
validation_loss = []
for epoch in range(n_epoch):
epoch_loss = 0
epoch_val_loss = 0
train_iter.reset()
test_iter.reset()
n_batch_train = 0
for batch in train_iter:
n_batch_train +=1
data = batch.data[0].as_in_context(mx.cpu())
with autograd.record():
loss = net(data)
loss.backward()
trainer.step(data.shape[0])
epoch_loss += nd.mean(loss).asscalar()
n_batch_val = 0
for batch in test_iter:
n_batch_val +=1
data = batch.data[0].as_in_context(mx.cpu())
loss = net(data)
epoch_val_loss += nd.mean(loss).asscalar()
epoch_loss /= n_batch_train
epoch_val_loss /= n_batch_val
training_loss.append(epoch_loss)
validation_loss.append(epoch_val_loss)
"""if epoch % max(print_period, 1) == 0:
print('Epoch {}, Training loss {:.2f}, Validation loss {:.2f}'.\
format(epoch, epoch_loss, epoch_val_loss))"""
end = time.time()
print('Training completed in {} seconds.'.format(int(end-start)))
Training completed in 62 seconds.
dataset_total_df['Date'] = dataset_ex_df['Date']
vae_added_df = mx.nd.array(dataset_total_df.iloc[:, :-1].values)
print('The shape of the newly created (from the autoencoder) features is {}.'.format(vae_added_df.shape))
The shape of the newly created (from the autoencoder) features is (2265, 112).
오토인코더에서 112개의 추가 기능을 만들었습니다. 높은 수준의 기능(전체 패턴)만 갖고 싶기 때문에 PCA(Principal Component Analysis:주성분분석)를 사용하여 새로 생성된 112개 기능에 고유 포트폴리오를 만듭니다. 이렇게 하면 데이터의 차원(열 수)이 줄어듭니다. Eigen 포트폴리오의 설명 기능은 원래 112개 기능과 동일합니다.
참고 다시 한 번 말하지만 이는 순전히 실험적입니다. 설명된 논리가 성립할지 100% 확신할 수 없습니다. AI 및 딥 러닝의 다른 모든 것과 마찬가지로 이것은 예술이며 실험이 필요합니다.
# We want the PCA to create the new components to explain 80% of the variance
pca = PCA(n_components=.8)
x_pca = StandardScaler().fit_transform(vae_added_df)
principalComponents = pca.fit_transform(x_pca)
principalComponents.n_components_
84
따라서 분산의 80%를 설명하려면 84개(112개 중) 기능만이 필요합니다. 그러나 이것은 여전히 많은 것입니다. 따라서 지금은 오토인코더 생성 기능을 포함하지 않습니다. Dense나는 중간 레이어(마지막 레이어가 아님)에서 출력을 가져와 이를 30개의 뉴런이 있는 다른 레이어에 연결하는 오토인코더 아키텍처를 만드는 작업을 할 것 입니다. 따라서 1) 더 높은 수준의 기능만 추출하고 2) 열 수가 훨씬 적습니다.
그림 9: 간단한 GAN 아키텍처
독자가 주가 움직임을 예측하는 데 GAN을 사용하는 이유를 완전히 이해할 수 있도록 GAN의 작동 방식에 대한 균형을 잡고 높은 수준의 개요를 제공하려고 노력할 것입니다. GAN에 익숙하다면 이 섹션과 다음 섹션을 건너뛰어도 됩니다
GAN 네트워크는 Generator ($G$)와 Discriminator ($D$)의 두 가지 모델로 구성됩니다. GAN 훈련 단계는 다음과 같습니다.
함께 결합하면,디그리고G일종의 minmax 게임을 하는 것과 같습니다(생성기가 판별기를 속이 려고 시도하여 가짜 예제에 대한 확률을 높입니다. 즉, $\mathbb{E} {z \sim p {z}(z)} [\log ( 1 - D(G(z)))]$. Discriminator는 Generator에서 오는 데이터를 분리하려고 합니다.디(G(지)), $\mathbb{E} {x \sim p {r}(x)} [\log D(x)]$)를 최대화합니다. 그러나 분리된 손실 함수가 있으면 둘 다 어떻게 함께 수렴할 수 있는지 명확하지 않습니다(그래서 우리는 Wasserstein GAN과 같은 일반 GAN에 대한 일부 개선 사항을 사용합니다). 전체적으로 결합된 손실 함수는 다음과 같습니다.
엘(디,G)=이자형엑스~피아르 자형(엑스)[통나무디(엑스)]+이자형지~피지(지)[통나무(1-디(G(지)))]
GAN( Generative Adversarial Networks )은 최근 사실적인 이미지, 그림 및 비디오 클립을 만드는 데 주로 사용되었습니다. 우리의 경우와 같이 시계열 데이터를 예측하는 데 사용되는 GAN의 응용 프로그램은 많지 않습니다. 그러나 주요 아이디어는 동일해야 합니다. 우리는 미래 주식 움직임을 예측하려고 합니다. 미래에 GS 주식의 패턴과 행동은 거의 동일해야 합니다(완전히 다른 방식으로 운영되기 시작하거나 경제가 급격하게 변화하지 않는 한). 따라서 우리는 이미 가지고 있는 것과 유사한(물론 절대적으로 동일하지는 않음) 분포를 가질 미래를 위한 데이터를 '생성'하고자 합니다. 즉, 과거 거래 데이터입니다. 따라서 이론상으로는 작동해야 합니다.
우리의 경우 LSTM 을 시계열 생성기로 사용하고 CNN 을 판별자로 사용합니다.
참고: 다음 두 섹션에서는 GAN에 대한 경험이 있다고 가정합니다.
기존 GAN에 대한 최근 개선 사항이 Uber의 엔지니어링 팀에서 나왔고 이를 MHGAN( Metropolis-Hastings GAN )이라고 합니다. Uber의 접근 방식 뒤에 있는 아이디어는 Google과 University of California, Berkeley에서 만든 DRS ( Discriminator Rejection Sampling )라는 또 다른 접근 방식과 다소 유사합니다. 기본적으로 GAN을 훈련할 때 Generator($G$)를 더 잘 훈련시키기 위한 목적으로만 Discriminator($D$)를 사용합니다. 종종 GAN을 학습한 후에는디더 이상. 그러나 MHGAN과 DRS는디생성된 샘플을 선택하기 위해G실제 데이터 분포에 가깝습니다(MHGAN이 샘플링에 Markov Chain Monte Carlo(MCMC)를 사용한다는 점에서 약간의 차이가 있음)
MHGAN 은 _G(독립적인 노이즈 입력에서 생성됨G-지0에게지케이아래 그림에서). 그런 다음 K 개의 출력($x'_0$에서엑스케이′′) 수락 규칙(식별자에서 생성됨)에 따라 현재 샘플을 수락할지 또는 마지막으로 수락된 샘플을 유지할지 여부를 결정합니다. 마지막으로 보관된 출력은 다음의 실제 출력으로 간주되는 출력입니다.G.
참고 : MHGAN은 원래 pytorch에서 Uber에 의해 구현되었습니다. MXNet/Gluon으로만 옮겼습니다.
그림 10: MHGAN의 시각적 표현(원본 Uber 게시물 에서 ).
GAN 교육은 매우 어렵습니다. 모델이 수렴되지 않을 수 있으며 모드 붕괴가 쉽게 발생할 수 있습니다. 우리는 Wasserstein GAN- WGAN 이라는 수정된 GAN을 사용할 것입니다 .
다시 말하지만, 자세한 내용은 다루지 않겠지만 가장 주목할 만한 사항은 다음과 같습니다.
앞서 언급했듯이 생성기는 RNN(Recurrent Neural Network) 유형인 LSTM 네트워크입니다. RNN은 모든 이전 데이터 포인트를 추적하고 시간이 지남에 따라 발전하는 패턴을 캡처할 수 있기 때문에 시계열 데이터에 사용됩니다. 그 특성상 RNN은 기울기 소실 로 인해 많은 시간을 겪습니다 . 즉, 훈련 중에 가중치가 받는 변화가 너무 작아서 변경되지 않아 네트워크가 최소 손실로 수렴할 수 없게 됩니다. 그래디언트가 너무 커지는 경우를 관찰할 수 있습니다. 이를 그래디언트 폭발 이라고 하지만 이에 대한 해결책은 매우 간단합니다. 즉, 그래디언트가 일정한 수를 초과하기 시작하면 그래디언트를 잘라내는 것입니다(예: 그래디언트 클리핑). 이 문제를 해결하는 두 가지 수정 사항인 Gated Recurrent Unit(GRU ) 및 장단기 기억( LSTM ).
둘 사이의 가장 큰 차이점은 다음과 같습니다.
1) GRU에는 2개의 게이트(업데이트 및 재설정)가 있고 LSTM에는 4개의 게이트(업데이트, 입력, 무시 및 출력)가 있습니다.
2) LSTM은 내부 메모리 상태를 유지하지만 GRU는 그렇지 않습니다.
3) LSTM은 출력 게이트 전에 비선형성(시그모이드)을 적용하지만 GRU는 적용하지 않습니다.
대부분의 경우 LSTM과 GRU는 정확도 측면에서 유사한 결과를 제공하지만 GRU는 훈련 가능한 매개변수가 훨씬 적기 때문에 계산 집약도가 훨씬 낮습니다. 그러나 LSTM은 훨씬 더 많이 사용됩니다.
엄밀히 말하면 LSTM 셀(게이트)의 수학은 다음과 같습니다.
g티=탄(엑스티여엑스g+시간티-1여시간g+비g),
나티=σ(엑스티여엑스나+시간티-1여시간나+비나),
에프티=σ(엑스티여엑스에프+시간티-1여시간에프+비에프),
영형티=σ(엑스티여엑스영형+시간티-1여시간영형+비영형),
씨티=에프티⊙씨티-1+나티⊙g티,
시간티=영형티⊙탄(씨티),
어디⊙요소별 곱셈 연산자이며 모든엑스=[엑스1,엑스2,…,엑스케이]⊤∈아르 자형케이두 가지 활성화 기능:,
σ(엑스)=[11+경험치(-엑스1),…,11+경험치(-엑스케이)]]⊤,
탄(엑스)=[1-경험치(-2엑스1)1+경험치(-2엑스1),…,1-경험치(-2엑스케이)1+경험치(-2엑스케이)]⊤
LSTM 아키텍처는 매우 간단합니다. 하나의 LSTM레이어에는 112개의 입력 유닛(데이터 세트에 112개의 기능이 있으므로)과 500개의 숨겨진 유닛이 있고, 하나 Dense의 레이어에는 1개의 출력이 있습니다. 매일 가격입니다. 이니셜라이저는 Xavier이며 L1 손실을 사용합니다.
gan_num_features = dataset_total_df.shape[1]
sequence_length = 17
class RNNModel(gluon.Block):
def __init__(self, num_embed, num_hidden, num_layers, bidirectional=False, \
sequence_length=sequence_length, **kwargs):
super(RNNModel, self).__init__(**kwargs)
self.num_hidden = num_hidden
with self.name_scope():
self.rnn = rnn.LSTM(num_hidden, num_layers, input_size=num_embed, \
bidirectional=bidirectional, layout='TNC')
self.decoder = nn.Dense(1, in_units=num_hidden)
def forward(self, inputs, hidden):
output, hidden = self.rnn(inputs, hidden)
decoded = self.decoder(output.reshape((-1, self.num_hidden)))
return decoded, hidden
def begin_state(self, *args, **kwargs):
return self.rnn.begin_state(*args, **kwargs)
lstm_model = RNNModel(num_embed=gan_num_features, num_hidden=500, num_layers=1)
lstm_model.collect_params().initialize(mx.init.Xavier(), ctx=mx.cpu())
trainer = gluon.Trainer(lstm_model.collect_params(), 'adam', {'learning_rate': .01})
loss = gluon.loss.L1Loss()
LSTM 레이어에서 500개의 뉴런을 사용하고 Xavier 초기화를 사용합니다. 정규화를 위해 L1을 사용합니다. LSTMMXNet에서 인쇄한 대로 내부에 무엇이 있는지 봅시다 .
print(lstm_model)
RNNModel(
(rnn): LSTM(112 -> 500, TNC)
(decoder): Dense(500 -> 1, linear)
)
보시다시피 LSTM의 입력은 112개의 기능( dataset_total_df.shape[1])이며 LSTM 레이어의 500개 뉴런으로 이동한 다음 단일 출력인 주가 값으로 변환됩니다.
LSTM의 논리는 다음과 같습니다. 우리는 17( sequence_length)일의 데이터(다시 말하지만 데이터는 매일 GS 주식의 주가 + 그날의 다른 모든 기능(상관된 자산, 정서 등)임) 예측을 시도합니다. 18일.
다른 게시물에서는 다음과 같이 바닐라 LSTM을 수정하는 것이 더 유익한지 살펴보겠습니다.
가장 중요한 하이퍼파라미터 중 하나는 학습률입니다. 거의 모든 옵티마이저(예: SGD, Adam 또는 RMSProp)의 학습 속도를 설정하는 것은 수렴 속도와 네트워크의 궁극적인 성능을 모두 제어하기 때문에 신경망을 훈련할 때 매우 중요합니다. 가장 간단한 학습 속도 전략 중 하나는 훈련 프로세스 전체에서 고정된 학습 속도를 유지하는 것입니다. 작은 학습률을 선택하면 옵티마이저가 좋은 솔루션을 찾을 수 있지만 초기 수렴 속도가 제한됩니다. 시간 경과에 따라 학습률을 변경하면 이러한 상충 관계를 극복할 수 있습니다.
이와 같은 최근 논문 은 수렴 및 시간 측면에서 훈련 중 전역 학습률을 변경하는 이점을 보여줍니다.
class TriangularSchedule():
def __init__(self, min_lr, max_lr, cycle_length, inc_fraction=0.5):
self.min_lr = min_lr
self.max_lr = max_lr
self.cycle_length = cycle_length
self.inc_fraction = inc_fraction
def __call__(self, iteration):
if iteration <= self.cycle_length*self.inc_fraction:
unit_cycle = iteration * 1 / (self.cycle_length * self.inc_fraction)
elif iteration <= self.cycle_length:
unit_cycle = (self.cycle_length - iteration) * 1 / (self.cycle_length * (1 - self.inc_fraction))
else:
unit_cycle = 0
adjusted_cycle = (unit_cycle * (self.max_lr - self.min_lr)) + self.min_lr
return adjusted_cycle
class CyclicalSchedule():
def __init__(self, schedule_class, cycle_length, cycle_length_decay=1, cycle_magnitude_decay=1, **kwargs):
self.schedule_class = schedule_class
self.length = cycle_length
self.length_decay = cycle_length_decay
self.magnitude_decay = cycle_magnitude_decay
self.kwargs = kwargs
def __call__(self, iteration):
cycle_idx = 0
cycle_length = self.length
idx = self.length
while idx <= iteration:
cycle_length = math.ceil(cycle_length * self.length_decay)
cycle_idx += 1
idx += cycle_length
cycle_offset = iteration - idx + cycle_length
schedule = self.schedule_class(cycle_length=cycle_length, **self.kwargs)
return schedule(cycle_offset) * self.magnitude_decay**cycle_idx
schedule = CyclicalSchedule(TriangularSchedule, min_lr=0.5, max_lr=2, cycle_length=500)
iterations=1500
plt.plot([i+1 for i in range(iterations)],[schedule(i) for i in range(iterations)])
plt.title('Learning rate for each epoch')
plt.xlabel("Epoch")
plt.ylabel("Learning Rate")
plt.show()
기능과 신경망이 많으면 과적합을 방지하고 전체 손실을 염두에 두어야 합니다.
과적합을 방지하기 위해 몇 가지 기술을 사용합니다(LSTM뿐만 아니라 CNN 및 자동 인코더에서도).
복잡한 신경망을 구축할 때 또 다른 중요한 고려 사항은 편향-분산 절충입니다. 기본적으로 네트워크를 학습할 때 발생하는 오류는 바이어스, 분산 및 기약 오류 - σ(노이즈 및 무작위성으로 인한 오류)의 함수입니다. 절충의 가장 간단한 공식은 다음과 같습니다.
이자형아르 자형아르 자형영형아르 자형=비나ㅏ에스2+Vㅏ아르 자형나ㅏN씨이자형+σ
우리는 일반적으로 이미지와 관련된 작업(분류, 컨텍스트 추출 등)에 CNN을 사용합니다. 그들은 특징에서 특징을 추출하는 데 매우 강력합니다. 예를 들어 개 이미지에서 첫 번째 컨볼루션 레이어는 가장자리를 감지하고 두 번째는 원을 감지하기 시작하며 세 번째는 코를 감지합니다. 우리의 경우 데이터 포인트는 작은 추세를 형성하고, 작은 추세는 더 크게 형성되며, 추세는 패턴을 형성합니다. CNN의 기능 감지 기능은 GS 주가 움직임의 패턴에 대한 정보를 추출하는 데 사용할 수 있습니다.
CNN을 사용하는 또 다른 이유는 CNN이 공간 데이터에서 잘 작동하기 때문입니다. 즉, 데이터 포인트가 흩어져 있는 것보다 서로 더 가까운 데이터 포인트가 서로 더 관련이 있다는 의미입니다. 이는 시계열 데이터에 적용됩니다. 우리의 경우 각 데이터 포인트(각 기능에 대한)는 연속된 날에 대한 것입니다. 두 날이 서로 가까울수록 서로 더 관련이 있다고 가정하는 것은 당연합니다. 한 가지 고려해야 할 사항(이 작업에서 다루지 않음)은 계절성과 이것이 CNN의 작업을 어떻게 변경할 수 있는지입니다.
참고 : 이 노트북의 다른 많은 부분과 마찬가지로 시계열 데이터에 CNN을 사용하는 것은 실험적입니다. 수학적 또는 기타 증명을 제공하지 않고 결과를 검사합니다. 그리고 결과는 다른 데이터, 활성화 기능 등을 사용하여 달라질 수 있습니다.
그림 11: CNN 아키텍처의 상위 수준 개요.
GAN 내부의 CNN 코드는 다음과 같습니다.
num_fc = 512
# ... other parts of the GAN
cnn_net = gluon.nn.Sequential()
with net.name_scope():
# Add the 1D Convolutional layers
cnn_net.add(gluon.nn.Conv1D(32, kernel_size=5, strides=2))
cnn_net.add(nn.LeakyReLU(0.01))
cnn_net.add(gluon.nn.Conv1D(64, kernel_size=5, strides=2))
cnn_net.add(nn.LeakyReLU(0.01))
cnn_net.add(nn.BatchNorm())
cnn_net.add(gluon.nn.Conv1D(128, kernel_size=5, strides=2))
cnn_net.add(nn.LeakyReLU(0.01))
cnn_net.add(nn.BatchNorm())
# Add the two Fully Connected layers
cnn_net.add(nn.Dense(220, use_bias=False), nn.BatchNorm(), nn.LeakyReLU(0.01))
cnn_net.add(nn.Dense(220, use_bias=False), nn.Activation(activation='relu'))
cnn_net.add(nn.Dense(1))
# ... other parts of the GAN
CNN을 인쇄해 봅시다.
print(cnn_net)
Sequential(
(0): Conv1D(None -> 32, kernel_size=(5,), stride=(2,))
(1): LeakyReLU(0.01)
(2): Conv1D(None -> 64, kernel_size=(5,), stride=(2,))
(3): LeakyReLU(0.01)
(4): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)
(5): Conv1D(None -> 128, kernel_size=(5,), stride=(2,))
(6): LeakyReLU(0.01)
(7): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)
(8): Dense(None -> 220, linear)
(9): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)
(10): LeakyReLU(0.01)
(11): Dense(None -> 220, linear)
(12): Activation(relu)
(13): Dense(None -> 1, linear)
)
우리가 추적하고 최적화할 하이퍼파라미터는 다음과 같습니다.
우리는 200명 이상을 훈련시킬 것 epochs입니다.
GAN이 200 epochs에서 학습한 후 MAE(LSTM의 오류 함수인G) 동일한 하이퍼파라미터 세트로 계속 훈련의 하이퍼파라미터를 변경할지 여부를 결정하는 강화 학습에 보상 값으로 전달합니다. 나중에 설명하는 것처럼 이 접근 방식은 RL을 실험하기 위한 것입니다.
RL이 하이퍼파라미터를 업데이트하기로 결정하면 다음으로 가장 기대되는 하이퍼파라미터 세트를 제공하는 베이지안 최적화(아래에서 설명) 라이브러리를 호출합니다.
하이퍼파라미터 최적화에서 강화 학습을 사용하는 이유는 무엇입니까? 주식 시장은 항상 변합니다. GAN과 LSTM을 훈련하여 매우 정확한 결과를 생성하더라도 결과는 특정 기간 동안만 유효할 수 있습니다. 즉, 전체 프로세스를 지속적으로 최적화해야 합니다. 프로세스를 최적화하기 위해 다음을 수행할 수 있습니다.
참고 : 이 노트북의 전체 강화 학습 부분의 목적은 보다 연구 지향적입니다. GAN을 환경으로 사용하는 다양한 RL 접근 방식을 살펴보겠습니다. RL을 사용하지 않고 딥 러닝 모델에서 하이퍼파라미터 최적화를 성공적으로 수행할 수 있는 방법은 많습니다. 하지만... 왜 안돼.
참고 : 다음 여러 섹션에서는 RL, 특히 정책 방법 및 Q-러닝에 대한 지식이 있다고 가정합니다.
RL의 기본 사항을 설명하지 않고 여기에서 구현하는 특정 접근 방식의 세부 사항으로 건너뛸 것입니다. 우리는 전체 환경을 알지 못하므로 환경이 어떻게 작동하는지에 대한 정의된 모델이 없기 때문에 모델 없는 RL 알고리즘을 사용할 것입니다. 모델을 따르십시오. 우리는 모델 없는 RL의 두 하위 부문인 정책 최적화와 Q-러닝을 사용할 것입니다.
RL 알고리즘 구축의 중요한 측면 중 하나는 보상을 정확하게 설정하는 것입니다. 환경의 모든 측면과 환경과 에이전트의 상호 작용을 캡처해야 합니다. 보상 R 을 다음과 같이 정의합니다.
아르 자형이자형승ㅏ아르 자형디=2∗엘영형에스에스G+엘영형에스에스디+ㅏ씨씨유아르 자형ㅏ씨와이G,
어디엘영형에스에스G,ㅏ씨씨유아르 자형ㅏ씨와이G, 그리고엘영형에스에스디Generator의 손실과 정확도, Discriminator의 손실이 각각 입니다. 환경은 GAN과 LSTM 교육의 결과입니다. 다른 에이전트가 취할 수 있는 작업은 GAN의 하이퍼파라미터를 변경하는 방법입니다.디그리고G그물.
레인보우란?
Rainbow( 링크 )는 7개의 알고리즘을 함께 결합한 Q 학습 기반 오프 정책 심층 강화 학습 알고리즘입니다.
PPO ( Proximal Policy Optimization )는 정책 최적화 모델이 없는 유형의 강화 학습입니다. 다른 알고리즘을 구현하는 것이 훨씬 간단하고 매우 좋은 결과를 제공합니다.
PPO를 사용하는 이유는 무엇입니까? PPO의 장점 중 하나는 값을 통해 간접적으로가 아니라 정책을 직접 학습한다는 것입니다(Q Learning이 Q-값을 사용하여 정책을 학습하는 방식). 그것은 우리의 사용 사례에 적합하고 분포 확률(softmax가 출력으로 추가된 경우)을 학습할 수 있는(평균 및 표준 편차를 통해) 연속 작업 공간에서 잘 작동할 수 있습니다.
정책 기울기 방법의 문제는 단계 크기 선택에 매우 민감하다는 것입니다. 크기가 작으면 진행이 너무 오래 걸립니다(주로 2차 도함수 행렬이 필요하기 때문일 것입니다). 크기가 크면 노이즈가 많아 성능이 크게 저하됩니다. 입력 데이터는 정책의 변경으로 인해 비정적입니다(보상 및 관찰의 분포도 변경됨). 지도 학습과 비교할 때 잘못 선택한 단계는 다음 방문의 전체 분포에 영향을 미치기 때문에 훨씬 더 파괴적일 수 있습니다. PPO는 이러한 문제를 해결할 수 있습니다. 또한 PPO는 다른 접근 방식과 비교하여 다음과 같은 이점이 있습니다.
참고 : 연습의 목적을 위해 PPO 및 기타 포함된 RL 접근 방식의 연구 및 최적화에 너무 많이 들어가지 않을 것입니다. 그보다는 GAN, LSTM 및 CNN 모델에 대한 하이퍼파라미터 최적화 프로세스에 사용 가능한 것을 취하고 맞추려고 노력할 것입니다. 우리가 재사용하고 사용자 정의할 코드는 OpenAI에서 생성되며 여기에서 사용할 수 있습니다 .
강화 학습을 더 탐구하기 위한 몇 가지 아이디어:
최적의 하이퍼파라미터 조합을 찾기 위해 많은 시간이 소요될 수 있는 그리드 검색 대신 베이지안 최적화 를 사용합니다 . 우리가 사용할 라이브러리는 이미 구현되어 있습니다
코드의 다음 부분은 초기화만 보여줍니다.
# Initialize the optimizer
from bayes_opt import BayesianOptimization
from bayes_opt import UtilityFunction
utility = UtilityFunction(kind="ucb", kappa=2.5, xi=0.0)
from utils import plot_prediction
마지막으로 보이지 않는(테스트) 데이터가 프로세스의 여러 단계 후에 입력으로 사용될 때 LSTM의 출력을 비교할 것입니다.
plot_prediction('Predicted and Real price - after first epoch.')
plot_prediction('Predicted and Real price - after first 50 epochs.')
plot_prediction('Predicted and Real price - after first 200 epochs.')
RL은 10개의 에피소드에 대해 실행됩니다(우리는 eposide를 200개의 epoch에 대한 하나의 전체 GAN 교육으로 정의합니다.)
plot_prediction('Final result.')
이 노트는 전적으로 유익합니다. 이 노트북에 제시된 내용 중 어느 것도 특정 증권, 증권 포트폴리오, 거래 또는 투자 전략이 특정인에게 적합하다는 권장 사항을 구성하지 않습니다. 선물, 주식 및 옵션 거래는 상당한 손실 위험을 수반하며 모든 투자자에게 적합하지 않습니다. 선물, 주식 및 옵션의 가치는 변동될 수 있으며 그 결과 고객은 원래 투자한 것보다 더 많은 손실을 입을 수 있습니다.
모든 거래 전략은 귀하의 책임 하에 사용됩니다.
자료출처 : https://github.com/borisbanushev/stockpredictionai
국가 통계량(나라지표)중 인기지표 목록 (0) | 2022.12.04 |
---|---|
열연강판과 냉연강판에 대하여 (0) | 2022.12.04 |
2023 트렌트 코리아 핵심 내용 (0) | 2022.11.05 |
2022년 13대 주력산업 전망 (0) | 2022.01.15 |
2023년 철강가격동향(30년 철강가격 그래프) (0) | 2022.01.15 |