Mengapa ada perbedaan antara memprediksi set Validasi dan set Test?


8

Saya memiliki model XGBoost yang mencoba memprediksi apakah suatu mata uang akan naik atau turun pada periode berikutnya (5 menit). Saya memiliki dataset dari 2004 hingga 2018. Saya membagi data secara acak menjadi 95% kereta dan validasi 5% dan akurasi pada set Validasi hingga 55%. Ketika saya kemudian menggunakan model pada set Tes baru (data dari 2019), akurasi turun menjadi di bawah 51%.

Adakah yang bisa menjelaskan mengapa itu terjadi?

Maksud saya, saya berasumsi model itu tidak "melihat" (dilatih tentang) data validasi lebih daripada memiliki data uji sehingga dapatkah itu benar-benar overfitting?

Saya telah melampirkan model sederhana di bawah ini untuk menggambarkan. Yang satu memberikan 54% pada set validasi tetapi hanya 50,9% pada set tes .

Terima kasih atas bantuannya!

NB Satu teori yang saya miliki adalah, karena beberapa fitur bergantung pada data historis (misalnya rata-rata bergerak), itu bisa berupa kebocoran data. Saya kemudian mencoba untuk memperbaikinya dengan hanya sampel data yang bukan bagian dari menciptakan moving average. Misal jika ada rata-rata bergerak 3 periode saya kemudian tidak mencicipi / menggunakan baris data dari 2 periode lalu. Itu tidak mengubah apa pun sehingga tidak ada dalam model di bawah ini.

NB2 Model di bawah ini adalah versi sederhana dari apa yang saya gunakan. Alasan untuk set validasi bagi saya adalah bahwa saya menggunakan algoritma genetik untuk penyetelan hyperparameter tetapi semua yang dihapus di sini untuk kejelasan.

import pandas as pd
import talib as ta
from sklearn.utils import shuffle
pd.options.mode.chained_assignment = None
from sklearn.metrics import accuracy_score

# ## TRAINING AND VALIDATING
# ### Read in data
input_data_file = 'EURUSDM5_2004-2018_cleaned.csv'   # For train and validation
df = pd.read_csv(input_data_file)

# ### Generate features
#######################
# SET TARGET
#######################
df['target'] = df['Close'].shift(-1)>df['Close']       # target is binary, i.e. either up or down next period

#######################
# DEFINE FEATURES
#######################
df['rsi'] = ta.RSI(df['Close'], 14) 

# ### Treat the data
#######################
# FIND AND MAKE CATEGORICAL VARAIBLES AND DO ONE-HOT ENCODING
#######################
for col in df.drop('target',axis=1).columns:     # Crude way of defining variables with few unique variants as categorical
    if df[col].nunique() < 25:
        df[col] = pd.Categorical(df[col])

cats = df.select_dtypes(include='category')     # Do one-hot encoding for the categorical variables
for cat_col in cats:
    df = pd.concat([df,pd.get_dummies(df[cat_col], prefix=cat_col,dummy_na=False)],axis=1).drop([cat_col],axis=1)

uints = df.select_dtypes(include='uint8')
for col in uints.columns:                   # Variables from the one-hot encoding is not created as categoricals so do it here
    df[col] = df[col].astype('category')

#######################
# REMOVE ROWS WITH NO TRADES
#######################
df = df[df['Volume']>0]

#######################
# BALANCE NUMBER OF UP/DOWN IN TARGET SO THE MODEL CANNOT SIMPLY CHOOSE ONE AND BE SUCCESSFUL THAT WAY
#######################
df_true = df[df['target']==True]
df_false = df[df['target']==False]

len_true = len(df_true)
len_false = len(df_false)
rows = min(len_true,len_false)

df_true = df_true.head(rows)
df_false = df_false.head(rows)
df = pd.concat([df_true,df_false],ignore_index=True)
df = shuffle(df)
df.dropna(axis=0, how='any', inplace=True)

# ### Split data
df = shuffle(df)
split = int(0.95*len(df))

train_set = df.iloc[0:split]
val_set = df.iloc[split:-1]

# ### Generate X,y
X_train = train_set[train_set.columns.difference(['target', 'Datetime'])]
y_train = train_set['target']

X_val = val_set[val_set.columns.difference(['target', 'Datetime'])]
y_val = val_set['target']

# ### Scale
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()

cont = X_train.select_dtypes(exclude='category')                   # Find columns with continous (not categorical) variables
X_train[cont.columns] = sc.fit_transform(X_train[cont.columns])    # Fit and transform

cont = X_val.select_dtypes(exclude='category')                     # Find columns with continous (not categorical) variables
X_val[cont.columns] = sc.transform(X_val[cont.columns])            # Transform

cats = X_train.select_dtypes(include='category')
for col in cats.columns:
    X_train[col] = X_train[col].astype('uint8')

cats = X_val.select_dtypes(include='category')
for col in cats.columns:
    X_val[col] = X_val[col].astype('uint8')


# ## MODEL
from xgboost import XGBClassifier
model = XGBClassifier()
model.fit(X_train, y_train)

predictions = model.predict(X_val)
acc = 100*accuracy_score(y_val, predictions)
print('{0:0.1f}%'.format(acc))

# # TESTING
input_data_file = 'EURUSDM5_2019_cleaned.csv'   # For testing
df = pd.read_csv(input_data_file)

#######################
# SET TARGET
#######################
df['target'] = df['Close'].shift(-1)>df['Close']       # target is binary, i.e. either up or down next period
#######################
# DEFINE FEATURES
#######################
df['rsi'] = ta.RSI(df['Close'], 14)

#######################
# FIND AND MAKE CATEGORICAL VARAIBLES AND DO ONE-HOT ENCODING
#######################
for col in df.drop('target',axis=1).columns:     # Crude way of defining variables with few unique variants as categorical
    if df[col].nunique() < 25:
        df[col] = pd.Categorical(df[col])

cats = df.select_dtypes(include='category')     # Do one-hot encoding for the categorical variables
for cat_col in cats:
    df = pd.concat([df,pd.get_dummies(df[cat_col], prefix=cat_col,dummy_na=False)],axis=1).drop([cat_col],axis=1)

uints = df.select_dtypes(include='uint8')
for col in uints.columns:                   # Variables from the one-hot encoding is not created as categoricals so do it here
    df[col] = df[col].astype('category')

#######################
# REMOVE ROWS WITH NO TRADES
#######################
df = df[df['Volume']>0]
df.dropna(axis=0, how='any', inplace=True)

X_test = df[df.columns.difference(['target', 'Datetime'])]
y_test = df['target']

cont = X_test.select_dtypes(exclude='category')                     # Find columns with continous (not categorical) variables
X_test[cont.columns] = sc.transform(X_test[cont.columns])            # Transform

cats = X_test.select_dtypes(include='category')
for col in cats.columns:
    X_test[col] = X_test[col].astype('uint8')

predictions = model.predict(X_test)
acc = 100*accuracy_score(y_test, predictions)
print('{0:0.1f}%'.format(acc))

Jawaban:


6

Satu-satunya perbedaan tampaknya adalah data. Mungkin set tes (yang merupakan data terbaru) sedikit berbeda dari set pelatihan / validasi dan menyebabkan kinerja yang kurang dari model Anda.


6

Hal yang paling mungkin adalah bahwa ada beberapa pergeseran konsep. Karena model Anda dilatih tentang data hingga 2018 dan diuji pada 2019, banyak hal telah berubah, dan beberapa dari perubahan ini model Anda mungkin tidak dapat diramalkan.

Beberapa kemungkinan lain:

Anda mengatakan Anda melakukan penyetelan hyperparameter, tetapi mengabaikannya dari kode untuk kesederhanaan. Tetapi jika Anda menggunakan set validasi untuk memilih hyperparameters, maka skor yang Anda dapatkan akan menjadi bias secara optimis. (Tapi Anda mengatakan model belum melihat set validasi, jadi mungkin ini bukan bagaimana Anda melakukannya.)

Akhirnya, mungkin Anda telah melakukan segalanya dengan benar, dan sebenarnya tidak ada penyimpangan konsep yang terjadi, tetapi efek acak itu hanya menjelaskan beberapa poin akurasi.


2

Ada dua alasan utama:

  1. Model yang dilatih memiliki kinerja yang mendekati acak. Sebagai contoh, 50% adalah kinerja acak dalam tugas klasifikasi biner dengan asumsi keanggotaan kelas yang sama. Dengan kata lain, model tidak belajar pola prediksi yang bermakna dari tahun 2004 hingga 2018 data.

  2. Mungkin ada pola baru di data 2019. Pola (hampir tidak dipelajari) dari data 2004 hingga 2018 tidak ditransfer ke data 2019.


Oh, ya, entah bagaimana saya melewatkan bahwa ini adalah klasifikasi biner, bahwa skor yang dilaporkan adalah akurasi, dan hanya 54% dan 51%. +1
Ben Reiniger

0

Seperti mantra investasi lama berjalan, "kinerja masa lalu bukan merupakan indikasi kinerja masa depan".

Kandidat utama saya overfitting. Sementara kesempatan untuk pola tertentu untuk menjadi gejala dari arah tertentu meskipun tidak kausal (atau prediksi di luar sampel yang ada) sama sekali kecil secara astronomis, ada juga sejumlah pola astronomi yang dapat dideteksi yang dapat menunjukkan perilaku seperti itu. .

Mari kita asumsikan mereka adalah pola nyata yang Anda pelajari:
Ketika Anda sedang melatih algo mempelajari dasar tiga dan kepala dan pundaknya ratusan bank juga, dan melakukannya lebih cepat daripada Anda dan menggunakan informasi itu.
Informasi itu tercermin dalam pergerakan harga yang berbeda, karena mereka tahu lebih banyak daripada tahun 2018 dan bertindak secara berbeda, model Anda belum tahu untuk mengambil tindakan tersebut karena mereka masih baru.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.