'금융' 관련 머신러닝 사례를 찾아보다가
ChatGPT로 주식 가격과 코인 가격을 예측해 본 아티클을 발견했다.
찬찬히 읽어보았는데,
시도해보면 재미있을 것 같아서 테슬라로 간단히 시도해보았다.
우선, 내가 참고한 아티클은
카카오뱅크 기술기획팀 William에서 발행한 글인데,
ChatGPT를 뉴스 분석에 활용하여 주가를 예측하는 방법의 효과성을 검증한 논문을 소개한다.
사실 더 흥미로운 건, 이 다음 아티클에서 “카카오뱅크에서 ChatGPT를 이용한 암호화폐의 가격 예측에 관해 연구한 내용“을 소개하는데, 논문까지 있어서 한번 읽어보기 좋다.
카카오뱅크, 'CahtGPT로 주식 가격 예측하기'
ChatGPT로 주식 가격 예측하기
OpenAI의 ChatGPT가 금융시장 분석에 미치는 영향을 연구한 최신 논문을 소개합니다. 이 논문은 ChatGPT를 활용한 뉴스 감성 분석이 주식 시장 예측에 얼마나 효과적인지 실증적으로 검토합니다. 카카
tech.kakaobank.com
먼저 이 프로젝트의 소회를 밝히면,
한동안 포트폴리오때문에 대체 어떤 분석 프로젝트를 넣을까 고민하면서
이것저것 시도해보는 와중이었다.
취준생의 입장이다보니
'이 길이 맞나?'라는 생각을 품은채 작업을 하다가
이 아티클을 읽고서 1초의 고민도 없이 '재미있겠다'라는 생각부터 들었다.
그리고 휘뚤마뚤 어떻게든 분석해보았다.
그리고 느낀 건, '파면 팔 수록 배울게 참 많다' 싶었다.
동시에 이론으로 휘리릭 훑고 지나갔던 것들을 다시 머릿속에서 재정립을 하는 시간이었다.
또,
갈피를 못잡던 취준생에게 다시금 '나'를 일깨워준 프로젝트랄까.
시리즈는 크게 두개로 나누었고,
1) 테슬라 주가 예측
2) 테슬라 뉴스기사 감성분석을 통한 주가 예측
이다.
그리고 작업하다보니
계속 이것도, 저것도 시도해서 스케일이 커질뻔 했지만;
얼른 붙잡아서 딱 위의 개념으로만 정리했다.
하면서 새로운 모델들도 많이 배웠고, 시도해봤는데
역시 ...'금융'쪽 데이터 다루는게 내 적성에 제일 잘 맞는 듯 하다 😉
프로젝트 목표
이 프로젝트는 '테슬라(TSLA)' 주식 가격을 예측하기 위해 다양한 머신러닝 및 딥러닝 기법을 활용하는 것을 목표로 한다.
- 과거 주가 데이터를 분석하여 시장 변동성과 추세를 파악
- 다양한 기술적 지표(이동평균, RSI, MACD, 볼린저 밴드 등)를 활용한 예측 모델 개발
- 여러 머신러닝 알고리즘(랜덤 포레스트, XGBoost, LSTM)의 성능 비교
- 뉴스 기사의 감성 분석을 통한 주가 예측 정확도 향상
개념적 설계
프로젝트는 다음과 같은 개념적 설계를 기반으로 했다.
- 데이터 수집 및 전처리:
- yfinance 라이브러리(*Yahoo Finance에서 제공하는 Python 패키지)를 사용하여 테슬라(TSLA) 주가 데이터 수집
- 기술적 지표 계산 및 특성 엔지니어링
- 뉴스 헤드라인 데이터 수집 및 감성 분석
- 특성 엔지니어링:
- 이동평균(MA_10, MA_50)
- 상대강도지수(RSI)
- 변동성 지표
- MACD(이동평균수렴발산): 단기 및 장기 지수이동평균(EMA)의 차이를 이용한 모멘텀 지표
- 볼린저 밴드: 주가의 변동성을 기반으로 한 기술적 지표로, 가격의 과매수/과매도 상태를 판단함
- 거래량 데이터
- 모델링 접근법:
- 기본 비교 모델로 랜덤 포레스트(Random Forest) 사용
- 트리 기반 머신러닝 모델(XGBoost)
- 앙상블 모델
- 딥러닝 기반 순환 신경망(LSTM)
- GridSearchCV를 통한 하이퍼파라미터 최적화
- 감성 분석 통합:
- 뉴스 헤드라인에 대한 감성 분석
- 감성 점수와 주가 움직임 간의 상관관계 분석
- 감성 정보를 예측 모델에 통합
시도한 방법
1. 데이터 수집 및 탐색
- yfinance 라이브러리를 사용하여 2024-03-13~2025-03-14 (1년)의 테슬라 주가 데이터 수집
- 참고로, 트럼프 대통령의 당선일은 2025년 1월 20일로, 테슬라 주가가 급상승한 날이다.
- 결측치 처리 및 이상치 탐지
- 기술적 지표 계산 및 특성 추가
# 📌 1. 테슬라(TSLA) 주가 데이터 가져오기
ticker = "TSLA"
df = yf.download(ticker, start="2020-03-13", end="2025-03-14")
위의 코드로 테슬라 주가를 가져오면 아래와 같이 'multiindex'로 되어 있는 걸 알 수 있다.
특성 엔지니어링(Feature Engineering)
아래의 특성 엔지니어링에서 생성한 지표들은 금융 도메인에서 주로 활용되는 지표이다.
- 10일 및 50일 이동평균선
- 지정된 기간 동안의 주가 평균을 계산함 -> 주가의 단기적 변동성을 줄이고, 추세를 파악하는데 사용됨
- MA_10: 최근 10일간의 평균 가격으로, 단기 추세 반영
- MA_50: 최근 50일간의 평균 가격으로, 중기 추세 반영
- -> 두 이동평균선의 교차(골든 크로스, 데드 크로스)는 중요한 매매 신호로 활용됨
- 지정된 기간 동안의 주가 평균을 계산함 -> 주가의 단기적 변동성을 줄이고, 추세를 파악하는데 사용됨
- 변동성 지표(10일 표준편차)
- 주가의 변화 정도를 측정, 일반적으로 수익률(퍼센트 변화)의 표준편차로 계산됨
- 높은 변동성: 주가가 크게 오르내리는 불안정한 상태
- 낮은 변동성: 주가가 안정적으로 움직이는 상태
- -> 10일 기간은 최근의 시장 상황을 반영하는 적절한 기간
- 주가의 변화 정도를 측정, 일반적으로 수익률(퍼센트 변화)의 표준편차로 계산됨
- MACD(12일 및 26일 지수이동평균 기반)
- 단기 지수이동평균(EMA)와 장기 지수이동평균의 차이를 나타내는 지표 -> 최근 데이터에 더 높은 가중치를 부여하는 이동평균
- 양수 MACD: 단기 추세가 장기 추세보다 강함 (상승 추세)
- 음수 MACD: 장기 추세가 단기 추세보다 강함 (하락 추세)
- MACD 교차: 추세 전환의 신호로 해석됨
- 단기 지수이동평균(EMA)와 장기 지수이동평균의 차이를 나타내는 지표 -> 최근 데이터에 더 높은 가중치를 부여하는 이동평균
- 볼린저 밴드(상단, 하단)
- 이동평균선을 중심으로 표준편차의 배수만큼 위아래로 밴드를 형성하는 지표 -> 주가의 변동성과 상대적 가격 수준을 시각화함
- Upper_Band: 평균 + 2×표준편차, 통계적으로 주가가 이 수준을 넘을 확률은 약 5%
- Lower_Band: 평균 - 2×표준편차, 통계적으로 주가가 이 수준 아래로 내려갈 확률은 약 5%
- 밴드 수축: 낮은 변동성, 종종 큰 가격 움직임의 전조
- 밴드 확장: 높은 변동성, 강한 추세의 신호
- 이동평균선을 중심으로 표준편차의 배수만큼 위아래로 밴드를 형성하는 지표 -> 주가의 변동성과 상대적 가격 수준을 시각화함
데이터 변환
- 거래량 로그 변환(Volume Log Transform)
- 거래량에 로그 함수를 적용해 분포를 정규화하는 기법 -> 거래량은 최대값과 최소값의 차이가 크기 때문에 로그 변환이 유용함
- 로그 변환된 거래량은 원래 거래량의 상대적 변화를 나타냄
- 1을 더하는 이유는 거래량이 0일 때 로그 계산이 불가능한 문제를 방지하기 위함
- 거래량에 로그 함수를 적용해 분포를 정규화하는 기법 -> 거래량은 최대값과 최소값의 차이가 크기 때문에 로그 변환이 유용함
결측값 처리
- 결측값 제거
- 결측값이 있는 행 제거 -> 계산 오류 방지
- 이동평균, 변동성 등의 계산 시 초기 n일 동안은 결측값이 발생함
- 이러한 결측값이 있는 행을 제거하여 완전한 데이터셋만 사용
- 결측값이 있는 행 제거 -> 계산 오류 방지
import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
# 📌 2. 기술적 지표 추가
df["MA_10"] = df["Close"].rolling(window=10).mean() # 10일 이동평균
df["MA_50"] = df["Close"].rolling(window=50).mean() # 50일 이동평균
df["Volatility"] = df["Close"].pct_change().rolling(window=10).std() # 변동성
# MACD 계산
df["EMA_12"] = df["Close"].ewm(span=12, adjust=False).mean()
df["EMA_26"] = df["Close"].ewm(span=26, adjust=False).mean()
df["MACD"] = df["EMA_12"] - df["EMA_26"]
# 볼린저 밴드 계산
df["Rolling_STD"] = df["Close"].rolling(window=10).std() # 표준편차 계산 후 별도 저장
df["Upper_Band"] = df["MA_10"] + (df["Rolling_STD"].values.flatten() * 2)
df["Lower_Band"] = df["MA_10"] - (df["Rolling_STD"].values.flatten() * 2)
# 거래량을 로그 스케일로 변환
df["Volume_log"] = np.log(df["Volume"] + 1)
# 결측값 제거
df.dropna(inplace=True)
데이터 수집을 통해 테슬라 주가를 가져오면 아래와 같이 'multiindex'로 되어 있는 걸 알 수 있다.
처음에는 첫 번째 레벨만 남기고 Random Forest와 XGBoost를 진행했는데 결과가 너무 안좋게 나온 것이다.
- 📌 선형 회귀 RMSE: 26.73, R² Score: -12.8745
- 📌 기본 랜덤 포레스트 RMSE: 5.27, R² Score: 0.4603
- 📌 기본 XGBoost RMSE: 19.04, R² Score: -6.0365
XGBoost의 R²값이 -6이나 나왔길래...아니, 이게 맞나??? 생각했다.
처음엔 모델의 문제인 줄 알고, GridSearchCV를 활용해서 Hyperparameter Tuning을 진행했다.
그런데..갈수록 성능이 더 안좋아지는 것이다.
그래서 혹시나 Multiindex문제인가 싶어서 claude에게 물어보니 그럴 수 있다하여
처음부터 다시 시작했다.
그랬더니 성능이 나쁘지 않게 나왔다. (휴)
2. 데이터 분할
시간 기반 분할
- 시간 순차적 분할(=비율 기반 시간 분할) -> 시간 순서대로 유지하면서 특정 비율(예: 80:20)로 훈련 세트와 테스트 세트를 나눔
- 시간적 일관성:
- 훈련 데이터는 항상 테스트 데이터보다 시간적으로 앞서 있음 -> 이는 "과거로 미래를 예측한다"는 예측 모델링의 기본 원칙을 따름
- 비율 기반 유연성:
- 80:20 비율은 일반적이지만, 데이터 크기와 예측 목적에 따라 조정 가능 -> 더 긴 훈련 기간(90:10)은 더 많은 패턴 학습을 가능하게 하고, 더 긴 테스트 기간(70:30)은 더 다양한 시장 상황에서의 성능 평가를 가능하게 함
- 단일 테스트 기간:
- 하나의 연속된 테스트 기간을 사용 -> 특정 시장 상황(상승장, 하락장, 횡보장)에서의 모델 성능을 평가
- 실용적 단순성:
- 구현이 간단하고 직관적 -> 복잡한 교차 검증 방법에 비해 계산 효율성이 높음
- 시간적 일관성:
# 📌 3. 시간 기반 데이터 분할 (명시적 방법)
train_size = int(len(df) * 0.8)
train_data = df.iloc[:train_size]
test_data = df.iloc[train_size:]
# 특성 선택
features = [
("MA_10", ""),
("MA_50", ""),
("Volatility", ""),
("MACD", ""),
("Upper_Band", ""),
("Lower_Band", ""),
("Volume_log", "")]
X_train = train_data[features]
# 타겟 변수도 멀티인덱스로 지정 (두 번째 레벨이 'TSLA'인 경우)
y_train = train_data[("Close", "TSLA")]
X_test = test_data[features]
y_test = test_data[("Close", "TSLA")]
아래는 테슬라 주가의 시계열 추이와 훈련/테스트 데이터 분할 지점을 나타낸다.
특성 중요도를 시각화 해보면, 'MA_10, MA_50, Upper_Band'가 주요한 지 알 수 있다.
3. 모델 선택 및 학습
1) Random Forest:
- 기본 비교 모델로 사용
- 여러 트리를 결합하여 과적합을 방지하고, 비선형 패턴을 잘 포착하며, 금융 데이터에 주로 있는 이상치에 덜 민감하여 채택함
- 배깅(Bagging) 기반 앙상블 모델로, 여러 의사결정 트리의 예측을 평균내어 최종 예측 생성
- 과적합 방지: 여러 트리의 결합으로 개별 트리의 과적합 경향 감소
- 비선형 패턴 포착: 복잡한 비선형 관계를 효과적으로 모델링
- 부트스트랩 샘플링을 통해 각 트리마다 다른 훈련 데이터 부분집합을 사용
- 이상치 내성: 금융 데이터에 흔한 이상치에 상대적으로 덜 민감
- 특성 무작위 선택으로 각 분할 시 특성의 일부만 고려하여 다양성 확보
- 특성 중요도 제공: 각 특성의 예측 기여도를 쉽게 해석 가능
- 배깅(Bagging) 기반 앙상블 모델로, 여러 의사결정 트리의 예측을 평균내어 최종 예측 생성
- 여러 트리를 결합하여 과적합을 방지하고, 비선형 패턴을 잘 포착하며, 금융 데이터에 주로 있는 이상치에 덜 민감하여 채택함
- 특성 중요도 분석
2) XGBoost:
- 부스팅 기반 앙상블 모델 -> 일반적으로 다른 알고리즘보다 우수한 성능 제공
- 이전 모델의 오차를 순차적으로 보완하는 방식
- 정규화 기능: L1, L2 정규화를 통한 과적합 방지
- 그래디언트 부스팅의 최적화된 구현으로, 계산 효율성과 성능 향상에 초점
- 결측값 처리: 내장된 결측값 처리 메커니즘 제공
- 잔차(Residual) 학습을 통해 이전 모델이 놓친 패턴을 점진적으로 포착
- 다양한 목적 함수: 다양한 손실 함수 지원으로 문제에 맞는 최적화 가능
- 이전 모델의 오차를 순차적으로 보완하는 방식
- 하이퍼파라미터 튜닝을 통한 성능 최적화
- 과적합 방지를 위한 정규화 기법 적용
3) 앙상블 모델
- 모델 결합 기법 -> 여러 모델 통합하여 최종 예측 생성
- 개별 모델의 약점 상호보완
- 편향-분산 트레이드오프 최적화로 일반화 성능 향상
# 4. 모델 정의 및 학습
# 랜덤 포레스트 모델
rf_model = RandomForestRegressor(n_estimators=100, random_state=42, max_depth=10, min_samples_split=5)
rf_model.fit(X_train, y_train)
# XGBoost 모델
xgb_model = xgb.XGBRegressor(objective="reg:squarederror", n_estimators=100, learning_rate=0.1)
xgb_model.fit(X_train, y_train)
# 5. 예측 및 성능 평가
rf_pred = rf_model.predict(X_test)
xgb_pred = xgb_model.predict(X_test)
def evaluate_model(y_true, y_pred, model_name):
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
r2 = r2_score(y_true, y_pred)
print(f"📌 {model_name} RMSE: {rmse:.2f}, R² Score: {r2:.4f}")
evaluate_model(y_test, rf_pred, "Random Forest")
evaluate_model(y_test, xgb_pred, "XGBoost")
위 코드로 Random Forest와 XGBoost를 모델링했을 때, 아래와 같은 결과가 나왔다.
- 📌 Random Forest RMSE: 27.03, R² Score: 0.9016
- 📌 XGBoost RMSE: 27.42, R² Score: 0.8988
- R² = 0.90 의미
- 모델이 테슬라 주가 변동의 약 90%를 설명할 수 있다는 의미 -> 이는 금융 시계열 예측에서는 매우 높은 수준의 성능이라고 함
- RMSE = 27 의미
- 평균적으로 예측값이 실제값과 약 $27 차이 발생
- R² = 0.90 의미
정리해보면,
XGBoost가 Random Forest보다 성능이 좋다.
그리고 금융 시계열 예측에서 XGBoost의 R² = 0.8988 는 상당히 좋은 성능이라고 한다. (주가는 변동성과 무작위성이 많아 50%이상이면 설명력을 가진다고 함)
그래도 차트를 보면
2025년 01월에 실제 주가와 예측 주가 차이가 많이 나는데,
바로...미국 대통령 공식 취임일이 1월 21일이라 그 전후로 '트럼프 대통령'에 대한 기대감으로 테슬라가 한창 핫해졌을 때이다.
바로 저걸 OpenAI로 감성분석을 진행하면 더 정교한 예측이 가능하지 않을까라고 생각했던 부분이다.
그래서 앙상블 모델을 추가적으로 구현해봤다.
- 📌 앙상블 모델 RMSE: 26.67, R² Score: 0.9042
- RMSE 감소:
- 랜덤 포레스트 대비: -0.36 (1.3% 개선)
- XGBoost 대비: -0.75 (2.7% 개선)
- R² 증가:
- 랜덤 포레스트 대비: +0.0026 (0.3% 개선)
- XGBoost 대비: +0.0054 (0.6% 개선)
- RMSE 감소:
근소하긴 하지만 단일 모델보다 성능이 향상된 것을 알 수 있었다.
다음으로는 각각의 주요 특성을 비교해보았는데, Random Forest와 XGBoost 모두 동일하게 'MA_10'이 가장 주요한 특성으로 채택되었고, 뒤이어 'Upper_band > Lower_Band' 순으로 나타났다.
다음으로는
예측 신뢰구간을 구해서 차트화 해보았다.
앙상블 모델(랜덤 포레스트, 부스팅 등)에서의 신뢰 구간
- 신뢰 구간 = 평균 예측값 ± z(α/2) × 예측값들의 표준편차
*z(α/2)는 표준 정규 분포의 임계값 (95% 신뢰 구간의 경우 1.96)
*평균 예측값은 모든 개별 모델 예측의 평균
*표준편차는 개별 모델 예측의 표준편차
lower_bound = rf_mean - 1.96 * rf_std upper_bound = rf_mean + 1.96 * rf_std
# 예측 신뢰구간 추정
# 랜덤 포레스트의 예측 신뢰 구간 추정
from sklearn.ensemble import RandomForestRegressor
# 여러 트리의 예측값 수집
n_trees = 100
rf_model_preds = []
for tree_idx in range(n_trees):
tree_pred = rf_model.estimators_[tree_idx].predict(X_test)
rf_model_preds.append(tree_pred)
# 예측 분포 계산
rf_preds_array = np.array(rf_model_preds)
rf_mean = np.mean(rf_preds_array, axis=0)
rf_std = np.std(rf_preds_array, axis=0)
# 95% 신뢰 구간
lower_bound = rf_mean - 1.96 * rf_std
upper_bound = rf_mean + 1.96 * rf_std
# 신뢰 구간 시각화
plt.figure(figsize=(15, 8))
plt.plot(y_test.index, y_test, label="Actual Price", color="blue")
plt.plot(y_test.index, rf_pred, label="Random Forest Prediction", color="red", linestyle="dashed")
plt.fill_between(y_test.index, lower_bound, upper_bound, color="red", alpha=0.2, label="95% Confidence Interval")
plt.xlabel("Date")
plt.ylabel("Tesla Stock Price (USD)")
plt.title("Tesla Stock Price Prediction with Confidence Interval")
plt.legend()
plt.show()
4. 딥러닝 모델 구현
1) LSTM(Long Short-Term Memory):
- 시계열 데이터에 특화된 딥러닝(RNN) 모델
- 2개의 LSTM 레이어와 드롭아웃 레이어로 구성
- 1층: LSTM(50, return_sequences=True)
- 드롭아웃(0.2)
- 2층: LSTM(50)
- 드롭아웃(0.2)
- 출력층: Dense(1)
- 1층: LSTM(50, return_sequences=True)
- 2개의 LSTM 레이어와 드롭아웃 레이어로 구성
- 데이터 처리
- 시퀀스 데이터 생성(window_size=10) -> 10일 데이터로 다음 날 예측
- 스케일링: MinMaxScaler 적용 (0-1 범위 정규화)
- 데이터 분할: 훈련 80%, 테스트 20% (시간 순서 유지)
- 훈련 설정
- 옵티마이저: Adam
- 손실 함수: MSE (Mean Squared Error)
- 배치 크기: 32
- 최대 에포크: 100
- 조기 종료: patience=10 (10 에포크 동안 개선 없으면 중단)
LSTM은 이 프로젝트를 진행하면서 알게된 모델이라 계속 찾아보면서 시도해보았다.
결과는 아래와 같이 나왔다.
- 📌 PyTorch LSTM 모델 RMSE: 18.01
- RMSE는 예측 오차의 크기를 원래 데이터와 같은 단위로 나타냄
- 테슬라 주가의 범위를 고려할 때, 이 값이 낮은지 높은지 판단하려면 주가의 평균이나 범위와 비교해야 한다.
예를 들어, 테슬라 주가가 평균 300달러라면 18.01달러의 오차는 약 6%로 상당히 정확한 예측이다.
- 테슬라 주가의 범위를 고려할 때, 이 값이 낮은지 높은지 판단하려면 주가의 평균이나 범위와 비교해야 한다.
- RMSE는 예측 오차의 크기를 원래 데이터와 같은 단위로 나타냄
- 📌 PyTorch LSTM 모델 R² Score: 0.9560
- 모델이 테스트 데이터의 변동성의 약 95.6%를 설명할 수 있음 -> 일반적으로 R² > 0.9는 매우 강한 예측력을 나타냄
- 주가 예측과 같은 복잡한 금융 시계열 데이터에서 이 정도의 R² 값은 탁월한 성능이라고 볼 수 있음
다른 모델들 과 비교해도 확연히 좋은 성능을 보이는 것을 알 수 있다.
Random Forest R² Score: 0.9016
XGBoost R² Score: 0.8988
LSTM: R² = 0.9560
전반적으로 손실 감소가 일관되어 학습은 안정적으로 진행된 것을 알 수 있다.
그리고 에포크는 80이후에서 loss가 증가하는 경향을 보여서
80으로 조기 종료를 시켜도 좋을 듯 하다.
LSTM의 예측 주가는 실제 가격과 거의 유사하게 움직이고 있다.
다만, 너무 유사하게 나와서...
과거 가격을 그대로 답습한게 아닐까 싶다.
그래서 서치해보니 바로 상단에 나오는 '딥러닝 초보들이 흔히하는 실수: 주식가격 예측'..ㄷㄷ
-> (칼럼) 딥러닝 초보들이 흔히하는 실수 : 주식가격 예측 AI by 코딩애플
제목부터 눌러보지 않을 수 없어 봤더니 아래와 같은 글이 써져있었다. ^^
아하..난 그래도 모델을 잘(?) 만들긴 했군 싶었다.
여기서 제안한 LLM도 있는데,
이걸 이제 뉴스 헤드라인 감성분석을 통해 진행해보고자 한다.
# 3. PyTorch LSTM 모델 정의
class LSTMModel(nn.Module):
def __init__(self, input_dim, hidden_dim, num_layers, output_dim):
super(LSTMModel, self).__init__()
self.hidden_dim = hidden_dim
self.num_layers = num_layers
# LSTM 레이어
self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True, dropout=0.2)
# 출력 레이어
self.fc = nn.Linear(hidden_dim, output_dim)
def forward(self, x):
# LSTM 출력
h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_dim).to(x.device)
c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_dim).to(x.device)
out, _ = self.lstm(x, (h0, c0))
# 마지막 시간 단계의 출력만 사용
out = self.fc(out[:, -1, :])
return out
# 4. PyTorch 텐서로 변환
X_train_tensor = torch.FloatTensor(X_train_seq)
y_train_tensor = torch.FloatTensor(y_train_seq.reshape(-1, 1))
X_test_tensor = torch.FloatTensor(X_test_seq)
# 5. 모델 초기화
input_dim = 1 # 특성 수
hidden_dim = 50 # 은닉층 크기
num_layers = 2 # LSTM 레이어 수
output_dim = 1 # 출력 크기
model = LSTMModel(input_dim, hidden_dim, num_layers, output_dim)
# 6. 손실 함수 및 옵티마이저
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 7. 모델 훈련
num_epochs = 100
batch_size = 32
n_batches = len(X_train_tensor) // batch_size
# 훈련 손실 기록
train_losses = []
model.train()
for epoch in range(num_epochs):
epoch_loss = 0
for i in range(n_batches):
start_idx = i * batch_size
end_idx = min(start_idx + batch_size, len(X_train_tensor))
batch_X = X_train_tensor[start_idx:end_idx]
batch_y = y_train_tensor[start_idx:end_idx]
# 순전파
outputs = model(batch_X)
loss = criterion(outputs, batch_y)
# 역전파 및 최적화
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch_loss += loss.item()
# 에포크당 평균 손실 계산
avg_loss = epoch_loss / n_batches
train_losses.append(avg_loss)
if (epoch+1) % 10 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.4f}')
# 8. 학습 곡선 시각화
plt.figure(figsize=(10, 6))
plt.plot(train_losses)
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.grid(True)
plt.show()
# 9. 예측
model.eval()
with torch.no_grad():
predictions = model(X_test_tensor).numpy()
# 10. 예측값을 원래 스케일로 변환
predictions = scaler.inverse_transform(predictions)
actual_values = scaler.inverse_transform(y_test_seq.reshape(-1, 1))
# 11. 평가
rmse = np.sqrt(mean_squared_error(actual_values, predictions))
r2 = r2_score(actual_values, predictions)
print(f'PyTorch LSTM 모델 RMSE: {rmse:.2f}')
print(f'PyTorch LSTM 모델 R² Score: {r2:.4f}')
# 12. 예측 결과 시각화
plt.figure(figsize=(12, 6))
plt.plot(actual_values, label='실제 가격', color='blue')
plt.plot(predictions, label='LSTM 예측', color='red', linestyle='--')
plt.title('PyTorch LSTM 모델 - 테슬라 주가 예측')
plt.xlabel('테스트 데이터 인덱스')
plt.ylabel('주가')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
그래서 이어지는 포스팅에는
테슬라 관련 헤드라인을 뽑아서 다양한 감성분석을 시도해보고자 한다.
킵고잉~✌