Skip to main content

머신러닝 데이터셋 만들기

Views 44약 8분생성형 AI 활용
Summary

모델보다 먼저 데이터셋을 설계한다

머신러닝 투자 실험에서 가장 먼저 할 일은 좋은 모델을 고르는 것이 아니다. 데이터셋을 제대로 만드는 것이다. 같은 랜덤 포레스트를 써도 데이터셋을 어떻게 만들었는지에 따라 결과는 완전히 달라진다. 특히 주식 데이터에서는 작은 실수 하나가 성과를 크게 부풀린다. 대표적인 실수가 미래 데이터를 몰래 넣는 것이다.

머신러닝 데이터셋은 크게 세 부분으로 나뉜다.

구성 요소의미예시
피처모델이 보고 판단하는 입력값최근 1개월 수익률, 변동성, 거래량 변화, PER
타깃모델이 맞히려는 정답다음 달 수익률, 다음 달 상승 여부
시간 정보언제 알 수 있었던 데이터인지 구분하는 기준2025-01-31, 2025-02-28

투자 데이터셋의 한 행은 보통 이런 의미를 가진다.

"2025년 1월 말까지 알 수 있었던 정보로, 2025년 2월의 수익률을 예측한다."

이 문장이 중요하다. 피처는 현재까지의 정보이고, 타깃은 미래의 결과다. 이 둘이 섞이면 모델은 예측한 것이 아니라 정답을 훔쳐본 것이 된다.

머신러닝 데이터셋 만들기AI 생성 이미지

피처란 무엇인가

피처는 모델이 사용하는 설명 변수다. 사람 투자자가 종목을 볼 때 가격 흐름, 거래량, 재무 지표, 시장 분위기를 참고하듯이 머신러닝 모델도 숫자로 정리된 정보를 입력받는다.

예를 들어 다음과 같은 피처를 만들 수 있다.

  • 최근 1개월 수익률

  • 최근 3개월 수익률

  • 최근 6개월 수익률

  • 20일 이동평균 대비 현재 가격의 위치

  • 60일 변동성

  • 최근 거래량 증가율

  • PER, PBR, ROE, 배당수익률

중요한 점은 피처가 모두 예측 시점에 실제로 알 수 있었던 값이어야 한다는 것이다. 3월 말에 발표된 재무제표를 1월 말 데이터에 넣으면 안 된다. 가격 데이터는 비교적 다루기 쉽지만, 재무 데이터는 발표 지연이 있기 때문에 더 조심해야 한다.

먼저 가격 데이터로 피처를 만드는 간단한 예를 보자. 여기서는 df에 Close와 Volume 열이 있다고 가정한다.

import pandas as pd
import numpy as np

# 일간 수익률
df["return_1d"] = df["Close"].pct_change()

# 최근 수익률 피처
df["return_20d"] = df["Close"].pct_change(20)
df["return_60d"] = df["Close"].pct_change(60)
df["return_120d"] = df["Close"].pct_change(120)

# 이동평균 대비 가격 위치
df["ma_20"] = df["Close"].rolling(20).mean()
df["ma_60"] = df["Close"].rolling(60).mean()
df["price_to_ma20"] = df["Close"] / df["ma_20"] - 1
df["price_to_ma60"] = df["Close"] / df["ma_60"] - 1

# 변동성 피처
df["volatility_20d"] = df["return_1d"].rolling(20).std()
df["volatility_60d"] = df["return_1d"].rolling(60).std()

# 거래량 변화
df["volume_ma20"] = df["Volume"].rolling(20).mean()
df["volume_change"] = df["Volume"] / df["volume_ma20"] - 1

이 코드는 가격 흐름, 추세, 위험, 거래량 정보를 숫자로 바꾼다. 모델은 차트를 눈으로 보지 못한다. 대신 이런 피처를 통해 "최근에 강했는가", "변동성이 컸는가", "거래량이 평소보다 늘었는가"를 학습한다.

밸류에이션 지표도 피처가 될 수 있다

가격 데이터만으로도 머신러닝 실험을 할 수 있지만, 투자에서는 재무 지표도 중요하다. PER, PBR, ROE, 배당수익률 같은 값은 기업의 가격과 질을 설명하는 피처가 된다.

예를 들어 여러 종목의 월말 데이터가 다음과 같이 정리되어 있다고 하자.

datetickercloseperpbrroedividend_yield
2025-01-31A500009.50.812.13.2
2025-01-31B8200024.03.118.50.7

이런 형태라면 가격 기반 피처와 재무 피처를 합쳐서 사용할 수 있다.

feature_cols = [
    "return_20d", "return_60d", "return_120d",
    "price_to_ma20", "price_to_ma60",
    "volatility_20d", "volatility_60d",
    "volume_change",
    "per", "pbr", "roe", "dividend_yield"
]

다만 재무 지표에는 두 가지 주의점이 있다.

첫째, 결측치가 많다. 모든 종목에 모든 지표가 항상 존재하지 않는다. 적자가 난 기업은 PER이 의미 없을 수 있고, 금융업처럼 일반 제조업과 다른 기준으로 봐야 하는 업종도 있다.

둘째, 발표 시점이 중요하다. 12월 말 재무제표라고 해서 12월 말에 알 수 있는 것은 아니다. 실제로는 다음 해에 공시된 뒤에야 투자자가 확인할 수 있다. 데이터셋을 정교하게 만들수록 "언제 알 수 있었는가"를 기록해야 한다.

초보 단계에서는 먼저 가격 데이터 중심으로 실험하고, 그다음 월별 또는 분기별 재무 지표를 천천히 붙이는 편이 안전하다.

타깃 변수 만들기: 무엇을 맞힐 것인가

타깃은 모델이 맞히려는 정답이다. 주식 예측에서 타깃을 잘못 정하면 모델이 아무리 좋아도 투자 전략으로 연결하기 어렵다.

가장 단순한 타깃은 다음 달 수익률이다.

"오늘까지의 정보로 앞으로 20거래일 동안 몇 퍼센트 오를까?"

코드로는 다음처럼 만들 수 있다.

# 앞으로 20거래일 수익률
future_return_20d = df["Close"].shift(-20) / df["Close"] - 1

df["target_return_20d"] = future_return_20d

여기서 shift(-20)은 20일 뒤의 종가를 현재 행으로 가져온다. 즉 현재 시점의 피처와 미래 20일 수익률을 같은 행에 놓는 것이다. 이것은 타깃을 만들기 위해 필요한 처리다. 단, 이렇게 만든 미래 수익률을 피처에 넣으면 안 된다.

회귀 문제로 만들 수도 있고, 분류 문제로 만들 수도 있다.

회귀 문제는 수익률 자체를 예측한다.

df["target"] = df["target_return_20d"]

분류 문제는 상승 여부를 예측한다.

df["target_up"] = (df["target_return_20d"] > 0).astype(int)

이 경우 타깃은 1 또는 0이다.

  • 1: 앞으로 20거래일 수익률이 0보다 크다

  • 0: 앞으로 20거래일 수익률이 0보다 작거나 같다

어느 쪽이 더 좋을까? 정답은 없다. 회귀는 예상 수익률의 크기를 얻을 수 있지만 잡음에 민감하다. 분류는 단순하고 해석하기 쉽지만, 0.1% 상승과 20% 상승을 모두 같은 "상승"으로 본다는 한계가 있다.

실전에서는 예측값 자체보다 그것을 어떻게 투자 규칙으로 바꾸는지가 더 중요하다. 예를 들어 다음 달 예상 수익률이 높은 상위 10개 종목만 매수하거나, 상승 확률이 55% 이상인 종목만 후보로 남길 수 있다.

행 단위가 아니라 시점 단위로 생각한다

주식 머신러닝 데이터는 보통 여러 종목과 여러 날짜가 섞인다. 예를 들어 월말마다 500개 종목의 피처를 만들면 한 달에 500행이 생긴다. 5년이면 약 30,000행이다.

이때 데이터셋은 다음과 같은 형태가 된다.

datetickerreturn_60dvolatility_20dpertarget_return_20d
2024-12-31A0.120.01811.20.04
2024-12-31B-0.050.02622.5-0.03
2025-01-31A0.080.01510.80.02

여기서 중요한 기준은 date다. 같은 날짜의 여러 종목은 같은 시점의 투자 후보군이다. 모델을 학습할 때도, 검증할 때도 이 시간 순서를 지켜야 한다.

결측치도 이 단계에서 정리한다.

model_df = df.dropna(subset=feature_cols + ["target_return_20d"])

X = model_df[feature_cols]
y = model_df["target_return_20d"]

dropna는 비어 있는 값을 제거한다. 다만 무작정 제거하면 특정 업종이나 특정 시기의 종목이 많이 빠질 수 있다. 결측치가 왜 생겼는지 확인하는 습관이 필요하다.

랜덤 셔플이 위험한 이유

일반적인 머신러닝 예제에서는 데이터를 무작위로 섞은 뒤 학습 데이터와 테스트 데이터로 나눈다. 이미지 분류나 설문 데이터에서는 이 방식이 자연스러울 수 있다. 하지만 주식 데이터에서는 위험하다.

이유는 간단하다. 투자는 시간의 순서 위에서 이루어진다. 2025년 데이터를 학습에 사용하고 2020년 데이터를 테스트하면, 실제 투자 상황과 반대가 된다. 미래를 배운 모델로 과거를 평가하는 셈이다.

잘못된 예는 다음과 같다.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, shuffle=True, random_state=42
)

이 코드는 데이터 순서를 섞는다. 주식 예측에서는 기본적으로 피해야 한다.

대신 시간순으로 나누어야 한다.

train_df = model_df[model_df["date"] < "2023-01-01"]
test_df = model_df[model_df["date"] >= "2023-01-01"]

X_train = train_df[feature_cols]
y_train = train_df["target_return_20d"]

X_test = test_df[feature_cols]
y_test = test_df["target_return_20d"]

이렇게 하면 2022년까지의 데이터로 학습하고, 2023년 이후를 검증한다. 실제 투자자가 과거 데이터로 모델을 만든 뒤 미래에 적용하는 구조와 같다.

검증 데이터와 테스트 데이터는 다르다

데이터를 나눌 때는 보통 세 구간을 생각한다.

구간역할
학습 데이터모델이 패턴을 배우는 구간
검증 데이터모델과 피처를 조정하는 구간
테스트 데이터마지막에 한 번만 확인하는 구간

예를 들어 2015년부터 2025년까지 데이터가 있다면 이렇게 나눌 수 있다.

  • 2015~2020년: 학습 데이터

  • 2021~2022년: 검증 데이터

  • 2023~2025년: 테스트 데이터

검증 데이터는 모델 선택에 사용된다. 랜덤 포레스트를 쓸지, 로지스틱 회귀를 쓸지, 피처를 몇 개 넣을지 비교하는 구간이다. 테스트 데이터는 최종 점검용이다. 테스트 성과를 보고 다시 모델을 고치기 시작하면 테스트 데이터도 사실상 검증 데이터가 된다. 그러면 실제 미래 성과를 가늠하기 어려워진다.

투자에서는 테스트 데이터의 의미가 크다. 우리가 궁금한 것은 "과거 일부 구간에서 잘 맞췄는가"가 아니라 "보지 못한 미래 구간에서도 통했는가"이기 때문이다.

워크포워드 검증

한 번만 학습하고 한 번만 테스트하면 특정 기간 운에 결과가 크게 흔들릴 수 있다. 그래서 시계열 데이터에서는 워크포워드 검증을 자주 사용한다. 워크포워드는 시간을 앞으로 밀어가며 여러 번 학습과 검증을 반복하는 방식이다.

예를 들어 다음과 같은 구조다.

회차학습 구간검증 구간
12015~2018년2019년
22015~2019년2020년
32015~2020년2021년
42015~2021년2022년

이 방식은 실제 운용과 비슷하다. 시간이 지나면 새 데이터를 추가해 모델을 다시 학습하고, 다음 기간에 적용한다.

간단한 흐름은 다음과 같다.

split_dates = ["2019-01-01", "2020-01-01", "2021-01-01", "2022-01-01"]
results = []

for split_date in split_dates:
    train_df = model_df[model_df["date"] < split_date]
    valid_df = model_df[
        (model_df["date"] >= split_date) &
        (model_df["date"] < pd.to_datetime(split_date) + pd.DateOffset(years=1))
    ]

    X_train = train_df[feature_cols]
    y_train = train_df["target_up"]

    X_valid = valid_df[feature_cols]
    y_valid = valid_df["target_up"]

    # model.fit(X_train, y_train)
    # pred = model.predict_proba(X_valid)[:, 1]
    # results.append(...)

여기서는 코드의 완성보다 구조가 중요하다. 학습 구간은 항상 검증 구간보다 과거여야 한다. 그리고 검증 구간의 결과를 합쳐 전체 성과를 확인한다.

워크포워드 검증은 번거롭지만 큰 장점이 있다. 특정 한 시기에만 잘 맞는 모델을 걸러낼 수 있다. 2020년에는 잘 됐지만 2021년과 2022년에 무너진 모델이라면 실전 전략으로 쓰기 어렵다.

데이터 누수 방지 원칙

데이터 누수는 모델이 예측 시점에 알 수 없던 정보를 학습하는 문제다. 투자 머신러닝에서 가장 조심해야 할 부분이다.

대표적인 누수 사례는 다음과 같다.

  1. 미래 수익률을 피처에 넣는 경우

  2. 전체 기간 평균과 표준편차로 피처를 표준화하는 경우

  3. 미래에 편입된 지수 구성 종목만 과거에도 사용한 경우

  4. 재무제표 발표일을 무시하고 회계 기준일만 사용하는 경우

  5. 테스트 데이터 성과를 보고 계속 모델을 수정하는 경우

특히 표준화는 초보자가 자주 놓친다. 예를 들어 전체 데이터의 평균과 표준편차를 사용해 피처를 변환하면 테스트 기간 정보가 학습 과정에 섞인다. 올바른 방식은 학습 데이터에서만 기준을 계산하고, 그 기준을 검증·테스트 데이터에 적용하는 것이다.

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()

X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

fit_transform은 학습 데이터에만 사용한다. transform은 이미 학습 데이터에서 계산한 기준을 다른 데이터에 적용한다. 이 작은 차이가 실험의 신뢰도를 결정한다.

예측 데이터셋의 최종 체크리스트

머신러닝 데이터셋을 만들었다면 모델 학습 전에 다음 질문을 확인하자.

  • 각 행의 피처는 해당 날짜에 실제로 알 수 있었는가?

  • 타깃은 피처보다 미래의 값인가?

  • 학습 데이터와 테스트 데이터를 시간순으로 나누었는가?

  • 랜덤 셔플을 사용하지 않았는가?

  • 결측치를 제거하거나 대체한 기준이 합리적인가?

  • 표준화, 랭킹, 점수화가 미래 정보를 사용하지 않았는가?

  • 재무 데이터의 발표 지연을 고려했는가?

  • 예측 결과를 어떤 매매 규칙으로 바꿀지 정했는가?

이 질문에 답할 수 있어야 모델 결과를 믿을 수 있다.

데이터셋은 투자 가설의 번역문이다

머신러닝 데이터셋을 만든다는 것은 단순히 표를 정리하는 일이 아니다. 투자 가설을 모델이 이해할 수 있는 언어로 번역하는 일이다.

"최근 강한 종목은 계속 강할까?"라는 질문은 최근 3개월·6개월 수익률 피처와 다음 달 수익률 타깃으로 바뀐다. "저평가된 우량주는 더 나은 성과를 낼까?"라는 질문은 PER, PBR, ROE 피처와 미래 수익률 타깃으로 바뀐다. "변동성이 낮은 종목은 방어적인가?"라는 질문은 변동성 피처와 하락장 성과 분석으로 바뀐다.

모델은 마법을 부리지 않는다. 우리가 만든 데이터셋 안에서만 배운다. 그래서 좋은 머신러닝 투자 실험은 좋은 질문, 안전한 데이터셋, 정직한 검증에서 시작한다.

다음 장에서는 이렇게 만든 데이터셋을 사용해 첫 번째 주식 예측 모델을 학습한다. 중요한 것은 정확도 숫자 하나가 아니다. 예측값을 투자 전략으로 바꾸고, 그 전략이 실제로 의미 있는 성과를 냈는지 확인하는 것이다.

책 목록

처음 만나는 AI 퀀트 투자: 파이썬과 AI로 투자 아이디어를 검증하는 법

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기