検証セットとテストセットの予測に違いがあるのはなぜですか?


8

XGBoostモデルで、通貨が次の期間(5分)に上昇または下降するかどうかを予測しようとしています。2004年から2018年までのデータセットがあります。ランダム化されたデータを95%トレインと5%検証に分割し、検証セットの精度は最大55%です。その後、新しいテストセット(2019年のデータ)でモデルを使用すると、精度が51%未満に低下します。

誰かがその理由を説明できますか?

つまり、モデルがテストデータよりも検証データを "見た"(トレーニングした)とは考えていません。そのため、モデルは本当に過剰適合しているのでしょうか?

以下に簡単なモデルを添付して説明します。検証セットで54%が得られますが、テストセットでは50.9%にすぎません

助けてくれてありがとう!

NB私が持っていた理論の1つは、一部の機能が履歴データ(移動平均など)に依存しているため、何らかのデータ漏えいの可能性があるというものでした。次に、移動平均の作成に含まれないサンプルデータのみを使用して、それを修正しようとしました。たとえば、3周期の移動平均がある場合、2周期前のデータ行をサンプリング/使用しません。それは何も変更しなかったので、以下のモデルにはありません。

NB2以下のモデルは、私が使用しているものの単純なバージョンです。私が検証セットを使用した理由は、ハイパーパラメーター調整に遺伝的アルゴリズムを使用しているためです。

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))

回答:


6

唯一の違いはデータのようです。おそらく、テストセット(最新のデータ)がトレーニング/検証セットのテストセットとわずかに異なり、モデルによってパフォーマンスが低下した可能性があります。


6

最も可能性が高いのは、コンセプトのドリフトがあったことです。モデルは2018年までのデータでトレーニングされ、2019年にテストされているため、状況が変更されており、これらの変更の一部はモデルで予測できない場合があります。

しかし、他の可能性のカップル:

ハイパーパラメータ調整を実行したと言いましたが、簡単にするためにコードから省略しています。ただし、ハイパーパラメーターを選択するために検証セットを使用している場合、得られるスコアは楽観的にバイアスされます。(しかし、モデルは検証セットを見たことがないと言うので、おそらくこれはあなたがそれをしている方法ではありません。)

最後に、すべてを正しく行った可能性があり、実際にコンセプトのドリフトが起こっているわけではありませんが、そのランダムな効果が正確さのいくつかのポイントを説明するだけです。


2

主な理由は2つあります。

  1. 訓練されたモデルは、ほぼランダムなパフォーマンスを持っています。たとえば、クラスメンバーシップが等しいと仮定したバイナリ分類タスクでは、50%がランダムなパフォーマンスです。言い換えると、モデルは2004年から2018年までのデータに意味のある予測パターンを学習しません。

  2. 2019年のデータには新しいパターンが存在する可能性があります。2004年から2018年のデータの(かろ​​うじて学習された)パターンは、2019年のデータに転送されません。


ああ、そうです、これはバイナリ分類であり、報告されたスコアは精度であり、54%と51%しかなかったので、どういうわけか見落としました。+1
ベンライニガー、

0

古い投資マントラのように、「過去のパフォーマンスは将来のパフォーマンスを示すものではありません」。

私の最有力候補は過剰適合です。因果関係がない(または手元のサンプルを超えて予測できない)にもかかわらず、特定のパターンが特定の方向の兆候を示す可能性は天文学的に小さい一方で、そのような動作を示す可能性のある検出されるパターンの天文学的な量もあります。 。

それらがあなたが学んだ本当のパターンであるとしましょう:
あなたがアルゴを訓練している間、その三重底と頭と肩を学ぶ何百もの銀行もそうでした、そしてあなたより速く、その情報を使用しました。
それらの情報はさまざまな価格変動に反映されています。なぜなら、彼らは2018年よりも多くのことを知っており、行動が異なっていたため、モデルはそれらが新しいため、まだそれらの行動を考慮に入れることを知りません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.