기본 콘텐츠로 건너뛰기

[matplotlib]quiver()함수

[stock]RandomForest를 사용한 주가 예측

RandomForest를 사용한 주가 예측

RandomForestClassifier

랜덤포레스트의 분류를 기법은 sklearn.ensemble.RandomForestClassifier()를 사용합니다.

  • n_estimators: 앙상블에 사용할 결정 트리의 개수. 일반적으로 클수록 모델의 성능이 향상되지만, 계산 비용도 증가. (기본값: 100)
  • criterion: 각 트리의 노드를 분할하는 데 사용할 함수. 'gini' (지니 불순도) 또는 'entropy' (정보 이득)를 사용할 수 있습니다. (기본값: 'gini')
  • max_depth: 각 트리의 최대 깊이를 제한합니다. 깊이가 깊어질수록 모델이 복잡해지고 과적합될 가능성이 높아집니다. None으로 설정하면 모든 리프 노드가 순수해질 때까지 또는 모든 리프 노드가 min_samples_split 샘플보다 적은 수의 샘플을 가질 때까지 트리가 확장됩니다. (기본값: None)
  • min_samples_split: 노드를 분할하는 데 필요한 최소 샘플 수입니다. 이 값보다 적은 샘플을 가진 노드는 더 이상 분할되지 않습니다. (기본값: 2)
  • min_samples_leaf: 리프 노드가 가져야 하는 최소 샘플 수입니다. 이 값보다 적은 샘플을 가진 리프 노드는 생성되지 않습니다. (기본값: 1)
  • max_features: 각 노드에서 분할을 위해 고려할 특성의 최대 개수입니다.
    • 'sqrt' (또는 'log2'): sqrt(n_features) 개의 특성을 사용합니다.
    • 'log2': log2(n_features) 개의 특성을 사용합니다.
    • None (또는 'auto'): 모든 특성을 사용합니다.
    • 정수 값: 지정된 개수의 특성을 사용합니다.
    • 부동 소수점 값 (0.0 ~ 1.0): 전체 특성 수의 해당 비율만큼 사용합니다. (기본값: 'sqrt')
  • bootstrap: 각 트리를 학습할 때 부트스트랩 샘플링을 사용할지 여부입니다. True이면 사용하고, False이면 전체 훈련 데이터셋을 사용합니다. (기본값: True)
    • bootstrap Sampling: 원본 데이터에서 중복을 허용하며 랜덤하게 일부 샘플을 선택하는 기법
  • oob_score: OOB (Out-of-Bag) 샘플을 사용하여 모델의 일반화 성능을 추정할지 여부입니다. True로 설정하면 학습 완료 후 OOB 점수가 계산됩니다. (기본값: False)
    • OOB(Out of Bag): 부트스트랩 샘플링에서 일부 데이터 포인트는 특정 결정트리에서의 학습에 포함시키지 않습니다. 이러한 샘플을 OOB 샘플이라 합니다. 이 샘플은 모델 검증에 사용되므로 학습데이터와 검증데이터의 분리가 일어나고 모델의 일반화(과적합 방지) 성능을 높이는데 도움이 됩니다.
  • n_jobs: 모델 학습 및 예측에 사용할 병렬 처리 코어 수입니다. -1로 설정하면 모든 가용 코어를 사용합니다. (기본값: None, 즉 1개의 코어 사용)
  • random_state: 난수 발생 시드를 설정하여 결과의 재현성을 확보합니다. (기본값: None)
  • class_weight: 훈련 데이터의 클래스에 가중치를 부여하는 데 사용됩니다. 불균형한 클래스 문제를 처리하는 데 유용합니다. 'balanced' 또는 딕셔너리 형태로 가중치를 지정할 수 있습니다. (기본값: None)

주요 메서드

  • fit(X, y): 훈련 데이터 X와 타겟 변수 y를 사용하여 모델을 학습합니다.
  • predict(X): 학습된 모델을 사용하여 새로운 데이터 X의 클래스를 예측합니다.
  • predict_proba(X): 각 샘플이 각 클래스에 속할 확률을 반환합니다.
  • score(X, y): 주어진 테스트 데이터 X와 타겟 변수 y에 대한 모델의 정확도를 반환합니다.
  • get_params([deep]): 모델의 파라미터를 딕셔너리 형태로 반환합니다.
  • set_params(**params): 모델의 파라미터를 설정합니다.
  • feature_importances_: 학습 후 각 특성의 중요도를 배열 형태로 반환합니다. 값이 클수록 해당 특성이 분류에 더 중요한 역할을 했다는 것을 의미합니다.
  • oob_score_: oob_score=True로 설정했을 때 계산되는 OOB 점수를 반환합니다.
  • oob_decision_function_: oob_score=True로 설정했을 때 각 훈련 샘플에 대한 OOB 예측의 결정 함수 결과를 반환합니다.
import numpy as np
import pandas as pd
import pandas_ta as ta
import matplotlib.pyplot as plt
import FinanceDataReader as fdr
import matplotlib.pyplot as plt

from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, mean_squared_error, r2_score

st=pd.Timestamp(2020, 1,1)
et=pd.Timestamp(2025, 5,16)
trgnme="000660"
trg=fdr.DataReader(trgnme,  st, et)
df=trg[["Open", "High", "Low", "Close", "Volume"]]
df.tail(1)
Open High Low Close Volume
Date
2025-05-16 206000 206750 203500 204500 2419507

분류를 위해 Close 자료를 대상으로 시차 1을 기준으로 증가(1), 감소(-1)를 계산한 결과를 반응변수로 합니다. 나머지 설명변수를 표준화합니다.

scaler=StandardScaler().fit(df)
X_T=scaler.transform(df)
X=X_T[:-1,:]
y=np.where(df["Close"]-df["Open"]>0, 1, -1)[1:]
y[:3]
array([-1,  1, -1])

학습과 검증 데이터를 분류하고 RandomForestClassifier()을 사용하여 분류 모델을 설정합니다. 이 함수에는 다양한 하이퍼변수가 필요하며 적절한 변수를 선정하기 위해 GridSearchCV() 함수를 사용합니다.

param_grid={'n_estimators': [5, 10, 15, 20], "max_depth":[5, 10, 20, None]}
rfc_search=GridSearchCV(RandomForestClassifier(random_state=3), param_grid, cv=5, scoring="accuracy")
rfc_search.fit(Xtr, ytr)
rfc_search.best_estimator_
RandomForestClassifier(max_depth=5, n_estimators=10, random_state=3)
rfc_search.score(Xtr, ytr), rfc_search.score(Xte, yte)
(0.6847237269772481, 0.5176767676767676)
rfc_search.predict(X_T[-1,:].reshape(-1, 5))
array([-1])

RandomForestRegressor

randomforestregression()은 다수의 결정트리(회귀모델)을 기반으로하는 앙상블 학습방법입니다. 다수의 회귀모델에서 각각의 예측을 수행하고 그 평균값을 최종 예측값으로 합니다. 이로인해 데이터의 일부와 무작위로 선택된 특징(설명변수)을 사용하여 트리(회귀모델)를 학습하기 때문에 과적합 위험을 감소시킵니다. 또한 결정 트리를 기반으로 하므로 데이터 내의 복잡한 비선형 관계 모델링에 적합합니다.

sklearn.ensemble.RandomForestRegressor() 클래스를 사용합니다.

  • n-estimators: 트리(모델)의 개수, 많을수록 성능향상 그러나 계산비용 증가 , 기본값=100
  • criterion: 각 트리의 노드를 분할하는 사용할 손실함수, 회귀에는 'squared_error'(기본값), 'absolute_error'를 사용
  • max_depth: 각 결정 트리의 최대 깊이. 깊어지면 모델의 복잡도 상승으로 과적합의 위험 증가. None(기본값)로 설정하면 min_samples_split보다 작은 샘플 수를 가질 때까지 분할
  • min_samples_split: 노드 분할을 위한 최소 샘플 수, 기본값 = 2
  • min_sampels_leaf: leaf 노드가 가져야 하는 최소 샘플 수 , 기본값=1
  • max_features: 각 노드에서 최적의 분할을 찾을 때 고려할 특징의 최대개수.
    • $\text{sqrt or auto}= \sqrt{\text{# of features}} $
    • 'log2': log2 만큼의 특징을 사용
    • [0, 1]사이의 실수를 지정하면 전체 특징의 수에 대한 비율을 사용
    • None or 1.0(기본값): 모든 특징의 수
  • max_samples: 각 트리에서 훈련 데이터의 비울 또는 개수.
  • random_state
  • n_jobs: 사용될 CPU 코어수. -1로 설정시 모든 코어를 사용

특정한 열을 반응변수, 나머지를 설명변수로 하고 설명과 반응변수의 시차를 1로 합니다. 설명과 반응변수를 표준화합니다. 설명변수의 마지막행을 최종 예측변수로 사용합니다.

ind=df.drop(labels=["Close"], axis=1).values
de=df["Close"].values
scalerX=StandardScaler().fit(ind[:-1, :])
X=scalerX.transform(ind[:-1,:])
Xfinal=scalerX.transform(ind[-1,:].reshape(-1, 4))
scalery=StandardScaler()
y=scalery.fit_transform(de[1:].reshape(-1,1)).flatten()
Xfinal
array([[ 2.04934765,  1.98281255,  2.07244882, -0.82600277]])

GridSearchCV() 함수를 사용하여 적정한 하이퍼매개변수를 설정하여 적절한 모델을 생성할 수 있습니다.

Xtr, Xte, ytr, yte=train_test_split(X, y, test_size=0.3, random_state=3)

reg_param_grid={'n_estimators': [10, 30, 60, 100, 120]}
reg_search=GridSearchCV(RandomForestRegressor(random_state=3), param_grid, cv=5, scoring="neg_mean_squared_error")
reg_search.fit(Xtr, ytr)
reg_search.best_estimator_
RandomForestRegressor(max_depth=5, n_estimators=20, random_state=3)

다음은 메서드 score()를 사용한 것으로 결과는 음의 평균 제곱오차(NMSE)입니다.

reg_search.score(Xtr, ytr), reg_search.score(Xte, yte)
(-0.006295633740480161, -0.011518694438607695)

위 모델에 대한 학습과 검증 데이터의 R2를 확인해 봅니다.

pred_tr=reg_search.predict(Xtr)
r2_score(ytr, pred_tr)
0.9935957464751347
pred_te=reg_search.predict(Xte)
r2_score(yte, pred_te)
0.9888857334889395

최종예측인자에 의한 예측

pre_final=reg_search.predict(Xfinal)
scalery.inverse_transform(pre_final.reshape(-1,1))
array([[202123.26656103]])

주가 예측은 Open, High, Low, Close의 모든 변수에 대해 이루어져야 합니다. 이를 위해 위 과정을 하나의 함수로 작성합니다.

def rfrPredict(df, deCol="Close", testSize=0.3, randomState=7):
    ind=df.drop(labels=deCol, axis=1).values
    de=df[deCol].values
    scalerX=StandardScaler().fit(ind[:-1, :])
    X=scalerX.transform(ind[:-1,:])
    Xfinal=scalerX.transform(ind[-1,:].reshape(-1, ind.shape[1]))
    scalery=StandardScaler()
    y=scalery.fit_transform(de[1:].reshape(-1,1)).flatten()
    Xtr, Xte, ytr, yte=train_test_split(X, y, test_size=testSize, random_state=randomState)
    reg_param_grid={'n_estimators': [10, 30, 60, 100, 120]}
    reg_search=GridSearchCV(RandomForestRegressor(random_state=randomState), reg_param_grid, cv=5, scoring="neg_mean_squared_error")
    reg_search.fit(Xtr, ytr)
    pre_final=reg_search.predict(Xfinal)
    pre=scalery.inverse_transform(pre_final.reshape(-1,1)).flatten()
    return pre

위 함수를 사용하여 위 데이터 df에 대해 시가, 고가, 저가, 종가의 최종예측은 다음과 같습니다.

result={}
for i in ["Open","High","Low", "Close"]:
    result[i]=rfrPredict(df, deCol=i)
result=pd.DataFrame(result)
result
Open High Low Close
0 207124.48211 206800.0 201173.869794 202123.266561

df에 기술적 지표 macd, adx, rsi를 적용하여 직전과 같은 예측치를 계산합니다.

data=trg.copy()
data["macd"]=data.ta.macd(fast=5, slow=20, signal=9).iloc[:,0]
data["adx"]=data.ta.adx().iloc[:,0]
data["rsi"]=data.ta.rsi()
data=data.dropna()
data.head(1)
Open High Low Close Volume Change macd adx rsi
Date
2020-02-12 100500 101000 99100 100000 2616573 0.002004 1067.067403 13.611534 59.511464
result2={}
for i in ["Open","High","Low", "Close"]:
    result2[i]=rfrPredict(data, deCol=i)
result2=pd.DataFrame(result2)
result2.round(0)
0 207976.0 209795.0 201843.0 205168.0

위 결과에 당일의 시가를 적용하여 고가, 저가, 종가를 예측합니다.

result2-(result2.values[0,0]-203000)
0 203000.0 204819.666667 196867.5 200192.5

댓글

이 블로그의 인기 게시물

[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' 와 같...

[sympy] Sympy객체의 표현을 위한 함수들

Sympy객체의 표현을 위한 함수들 General simplify(x): 식 x(sympy 객체)를 간단히 정리 합니다. import numpy as np from sympy import * x=symbols("x") a=sin(x)**2+cos(x)**2 a $\sin^{2}{\left(x \right)} + \cos^{2}{\left(x \right)}$ simplify(a) 1 simplify(b) $\frac{x^{3} + x^{2} - x - 1}{x^{2} + 2 x + 1}$ simplify(b) x - 1 c=gamma(x)/gamma(x-2) c $\frac{\Gamma\left(x\right)}{\Gamma\left(x - 2\right)}$ simplify(c) $\displaystyle \left(x - 2\right) \left(x - 1\right)$ 위의 예들 중 객체 c의 감마함수(gamma(x))는 확률분포 등 여러 부분에서 사용되는 표현식으로 다음과 같이 정의 됩니다. 감마함수는 음이 아닌 정수를 제외한 모든 수에서 정의됩니다. 식 1과 같이 자연수에서 감마함수는 factorial(!), 부동소수(양의 실수)인 경우 적분을 적용하여 계산합니다. $$\tag{식 1}\Gamma(n) =\begin{cases}(n-1)!& n:\text{자연수}\\\int^\infty_0x^{n-1}e^{-x}\,dx& n:\text{부동소수}\end{cases}$$ x=symbols('x') gamma(x).subs(x,4) $\displaystyle 6$ factorial 계산은 math.factorial() 함수를 사용할 수 있습니다. import math math.factorial(3) 6 a=gamma(x).subs(x,4.5) a.evalf(3) 11.6 simpilfy() 함수의 알고리즘은 식에서 공통사항을 찾아 정리하...

sympy.solvers로 방정식해 구하기

sympy.solvers로 방정식해 구하기 대수 방정식을 해를 계산하기 위해 다음 함수를 사용합니다. sympy.solvers.solve(f, *symbols, **flags) f=0, 즉 동차방정식에 대해 지정한 변수의 해를 계산 f : 식 또는 함수 symbols: 식의 해를 계산하기 위한 변수, 변수가 하나인 경우는 생략가능(자동으로 인식) flags: 계산 또는 결과의 방식을 지정하기 위한 인수들 dict=True: {x:3, y:1}같이 사전형식, 기본값 = False set=True :{(x,3),(y,1)}같이 집합형식, 기본값 = False ratioal=True : 실수를 유리수로 반환, 기본값 = False positive=True: 해들 중에 양수만을 반환, 기본값 = False 예 $x^2=1$의 해를 결정합니다. solve() 함수에 적용하기 위해서는 다음과 같이 식의 한쪽이 0이 되는 형태인 동차식으로 구성되어야 합니다. $$x^2-1=0$$ import numpy as np from sympy import * x = symbols('x') solve(x**2-1, x) [-1, 1] 위 식은 계산 과정은 다음과 같습니다. $$\begin{aligned}x^2-1=0 \rightarrow (x+1)(x-1)=0 \\ x=1 \; \text{or}\; -1\end{aligned}$$ 예 $x^4=1$의 해를 결정합니다. solve() 함수의 인수 set=True를 지정하였으므로 결과는 집합(set)형으로 반환됩니다. eq=x**4-1 solve(eq, set=True) ([x], {(-1,), (-I,), (1,), (I,)}) 위의 경우 I는 복소수입니다.즉 위 결과의 과정은 다음과 같습니다. $$x^4-1=(x^2+1)(x+1)(x-1)=0 \rightarrow x=\pm \sqrt{-1}, \; \pm 1=\pm i,\; \pm1$$ 실수...