Gere previsões ortogonais (não correlacionadas) para uma determinada variável

8

Eu tenho uma Xmatriz, uma yvariável e outra variável ORTHO_VAR. Preciso prever a yvariável usando X, no entanto, as previsões desse modelo precisam ser ortogonais e, ao ORTHO_VARmesmo tempo, estar o mais correlacionadas ypossível.

Eu preferiria que as previsões fossem geradas com um método não paramétrico como, por exemplo, xgboost.XGBRegressormas eu poderia usar um método linear se for absolutamente necessário.

Este código:

import numpy as np
import pandas as pd
from sklearn.datasets import make_regression
from xgboost import XGBRegressor

ORTHO_VAR = 'ortho_var'
TARGET = 'target'
PRED = 'yhat'

# Create regression dataset with two correlated targets
X, y = make_regression(n_features=20, random_state=245, n_targets=2)
indep_vars = ['var{}'.format(i) for i in range(X.shape[1])]

# Pull into dataframe
df = pd.DataFrame(X, columns=indep_vars)
df[TARGET] = y[:, 0]
df[ORTHO_VAR] = y[:, 1]

# Fit a model to predict TARGET
xgb = XGBRegressor(n_estimators=10)
xgb.fit(df[indep_vars], df[TARGET])
df[PRED] = xgb.predict(df[indep_vars])

# Correlation should be low or preferably zero
pred_corr_w_ortho = df.corr().abs()[PRED][ORTHO_VAR]
assert pred_corr_w_ortho < 0.01, "Correlation score: {0} is superior to the given threshold.".format(pred_corr_w_ortho)

Retorna isso:

---------------------------------------------------------------------------
AssertionError                           
      1 pred_corr_w_ortho = df.corr().abs()[PRED][ORTHO_VAR]
----> 2 assert pred_corr_w_ortho < 0.01, "Correlation score: {0} is superior to the given threshold.".format(pred_corr_w_ortho)

AssertionError: Correlation score: 0.5895885756753665 is superior to the given threshold.

... e gostaria de algo que mantenha a maior precisão preditiva possível, mantendo-se ortogonal ao ORTHO_VAR

Chris
fonte
2
Qual é a correlação do TARGET com ORTHO_VAR?
Esmailian 15/04/19
Boa pergunta. Eles são de fato correlacionados (digamos 50%); as previsões provavelmente sofrerão em termos de precisão ao serem ortogonais.
Chris

Respostas:

5

Este requisito pode ser atendido adicionando ruído suficiente às previsões y^ correlacioná-los de valores ortogonais v. Idealmente, sey^ já está correlacionado de v, nenhum ruído seria adicionado a y^, portanto y^ estaria maximamente correlacionado com y.

Matematicamente, queremos criar y^=y^+ϵ de ϵN(0,σϵ), satisfazer

ry^v=σy^vσy^σv<δ
para limiar arbitrário δ. Agora, vamos expandir essa desigualdade para encontrar um limite inferior para o padrão de ruídoϵ, ie σϵ.
σy^2=σy^2+σϵ2,σy^v=E[(y^+ϵ-μy^-μϵ=0 0)(v-μv)]=E[(y^-μy^)(v-μv)]+E[ϵ(v-μv)]=0 0=σy^v,ry^v=σy^vσy^σv=σy^vσvσy^2+σϵ2<δσy^(ry^vδ)2-1 1<σϵ

Como todas as variáveis ​​no lado esquerdo da desigualdade podem ser calculadas, podemos amostrar ruídos de N(0 0,σϵ) e adicione-os a y^ para satisfazer a desigualdade original.

Aqui está um código que faz exatamente a mesma coisa:

import numpy as np
import pandas as pd
from sklearn.datasets import make_regression
from xgboost import XGBRegressor

ORTHO_VAR = 'ortho_var'
IND_VARNM = 'indep_var'
TARGET = 'target'
CORRECTED_VARNM = 'indep_var_fixed'

seed = 245
# Create regression dataset with two correlated targets
X, y = make_regression(n_samples=10000, n_features=20, random_state=seed, n_targets=2)
indep_vars = ['var{}'.format(i) for i in range(X.shape[1])]

# Pull into dataframe
df = pd.DataFrame(X, columns=indep_vars)
df[TARGET] = y[:, 0]
df[ORTHO_VAR] = y[:, 1]

# Fit a model to predict TARGET
xgb = XGBRegressor(n_estimators=10)
xgb.fit(df[indep_vars], df[TARGET])
df['yhat'] = xgb.predict(df[indep_vars])

delta = 0.01

# std of noise required to be added to y_hat to bring the correlation
# of y_hat with ORTHO_VAR below delta
std_y_hat = np.std(df['yhat'], ddof=1)
corr_y_hat_ortho_var = np.corrcoef(df['yhat'], df[ORTHO_VAR])[1, 0]
corr_y_hat_target = np.corrcoef(df['yhat'], df[TARGET])[1, 0]
std_noise_lower_bound = std_y_hat * np.sqrt((corr_y_hat_ortho_var / delta)**2 - 1.0)
std_noise = max(0, std_noise_lower_bound) + 1
print('delta: ', delta)
print('std_y_hat: ', std_y_hat)
print('corr_y_hat_target: ', corr_y_hat_target)
print('corr_y_hat_ortho_var: ', corr_y_hat_ortho_var)
print('std_noise_lower_bound: ', std_noise_lower_bound)
print('std_noise: ', std_noise)

# add noise
np.random.seed(seed)
noises = np.random.normal(0, std_noise, len(df['yhat']))
noises -= np.mean(noises)  # remove slight deviations from zero mean
print('noise_samples: mean:', np.mean(noises), ', std: ', np.std(noises))
df['yhat'] = df['yhat'] + noises

# measure new correlation
corr_y_hat_ortho_var = np.corrcoef(df['yhat'], df[ORTHO_VAR])[1, 0]
corr_y_hat_target = np.corrcoef(df['yhat'], df[TARGET])[1, 0]
print('new corr_y_hat_target: ', corr_y_hat_target)
print('new corr_y_hat_ortho_var: ', corr_y_hat_ortho_var)
# Correlation should be low or preferably zero
assert corr_y_hat_ortho_var < delta, corr_y_hat_ortho_var
assert -delta < corr_y_hat_ortho_var, corr_y_hat_ortho_var

quais saídas:

delta:  0.01
std_y_hat:  69.48568725585938
corr_y_hat_target:  0.8207672834857673
corr_y_hat_ortho_var:  0.7663936356880843
std_noise_lower_bound:  5324.885500165032
std_noise:  5325.885500165032
noise_samples: mean: 1.1059455573558807e-13 , std:  5373.914830034988
new corr_y_hat_target:  -0.004125016071865934
new corr_y_hat_ortho_var:  -0.000541131379457552

Você pode experimentar com outros deltas. Ao comparar std_y_hatcom std_noise_lower_bound, você pode ver que um ruído enorme deve ser adicionado aoy^ para correlacioná-lo de v abaixo 0,01, que tolera drasticamente y^ de y também.

Nota: Assertionpode falhar para limites muito pequenosδ devido à contagem insuficiente de amostras.

Esmailiano
fonte
A falta de correlação entre o yhat e a variável alvo está relacionada à alta correlação entre as duas variáveis ​​alvo (por exemplo, minha culpa)? Idealmente gostaríamos new corr_y_hat_targetde ser o mais alto possível com new corr_y_hat_ortho_varser o mais baixo possível
Chris
@ Chris Indiretamente sim. Se targettivesse uma baixa correlação com orth, y_hat(que tem uma alta correlação com target) também teria uma baixa correlação com orth. Como resultado, um baixo ruído teria sido adicionado y_hate sua correlação com targetteria mudado um pouco.
Esmailian