Abrir en Google Colab
|
|
Descargar notebook
|
Clasificador de dominio (desviación covariable)
Una alternativa para detectar una desviación covariable es entrenando un clasificar binario el cual utilice todos los predictores de los conjuntos de datos origen y objetivo. Este clasificador intetará predecir a que conjunto de datos pertenece cada observación, es decir que nuestra variable objetivo será source y target. Si los dos conjuntos de datos son lo suficientemente diferentes, esto implicar[a que el clasificador podrá facilmente discriminar entre los dos conjuntos de datos y por
lo tanto mostrará una buena performance. Utilizaremos esto como un síntoma de que el conjunto de datos ha cambiado.
Para poder utilizar este método, necesitaremos realizar un test de hipótesis binomial el cual compruebe que tan probable es obtener tal performance en nuestro clasificador bajo la suposición de que no hay una desviación. Si el p-value (la probabilidad de que el modelo tenga al menos tal performance de clasificación bajo la suposición nula) es bajo, entonces esto significa que una desviación podría haber ocurrido (rechazamos la hipótesis nula en favor de la alternativa). En la práctica
dispararemos una alerta cuando el p-value del test de hipótesis binomial es menor que el nivel de significancia que estamos buscando.
Ejemplo
En este ejemplo, usaremos los datos de IRIS dataset para generar lotes con distribuciones distintas de los datos, lo que provocará una desviación en los datos que utilizaremos productivos. El conjunto de datos de IRIS es parte de la biblioteca sklearn que constan de 3 tipos diferentes de longitud de pétalo y sépalo (Setosa, Versicolour y Virginica), descriptos por la longitud del sépalo, el ancho del sépalo, la longitud del pétalo y el ancho del pétalo:
[4]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data[:,:4]
y = iris.target
Para ilustrar el problema, de forma tendenciosa, filtraremos nuestro conjunto de datos para que algunas muestras especificas no sean incluidas en el conjunto de entrenamiento. En este caso, seleccionaremos todas las muestras donde el predictor ancho del sépalo es mayor a 2.8. Veamos primero cual es la distribución de los valores y rangos de los predictores:
[59]:
df = pd.DataFrame(X, columns=['longitud del sepalo',
'ancho del sepalo',
'longitud del petalo',
'ancho del pétalo'])
df.describe()
[59]:
| longitud del sepalo | ancho del sepalo | longitud del petalo | ancho del pétalo | |
|---|---|---|---|---|
| count | 150.000000 | 150.000000 | 150.000000 | 150.000000 |
| mean | 5.843333 | 3.057333 | 3.758000 | 1.199333 |
| std | 0.828066 | 0.435866 | 1.765298 | 0.762238 |
| min | 4.300000 | 2.000000 | 1.000000 | 0.100000 |
| 25% | 5.100000 | 2.800000 | 1.600000 | 0.300000 |
| 50% | 5.800000 | 3.000000 | 4.350000 | 1.300000 |
| 75% | 6.400000 | 3.300000 | 5.100000 | 1.800000 |
| max | 7.900000 | 4.400000 | 6.900000 | 2.500000 |
Generemos los conjuntos de datos de entrenamiento filtrando entonces los valores que mencionamos anteriormente. Esto hará que nuestro proceso de entrenamiento nunca haya visto instancias de datos donde ancho del sepalo es menor que 2.8, y por lo tanto no sabrá como clasificar estas instancias. > Nota: Esta forma de separar los datos en conjuntos de entrenamiento es ficticia y se utiliza simplemente para demostrar el concepto.
[6]:
X_train, X_test = [data for _, data in df.groupby(df["ancho del sepalo"] <= 2.8)]
y_train = y[X_train.index]
y_test = y[X_test.index]
Entrenaremos un modelo para resolver el problema utilizando el conjunto de datos que seleccionamos.
[7]:
from sklearn import svm
from sklearn.model_selection import train_test_split
model = svm.SVC(C=1.0, kernel='linear', gamma=0.5, probability=True)
model = model.fit(X_train, y_train)
Simulando un cambio en el proceso de recolección de datos
Supongamos que cuando desplegamos el modelo en producción, efectivamente vemos la totalidad del conjunto de datos, es decir, con instancias que tienen valores que el modelo nunca vio. En este caso, es natural pensar que la performance del modelo se degradará, pero recordemos que en producción no tenemos acceso a los valores reales de la variable objetivo. Por lo tanto, deberemos decidir si nuestro modelo debería o no reentrenarse basandonos solo en los predictores.
Para realizar esto, generaremos un nuevo conjunto de datos que es la unión de los conjuntos de datos que se utilizaron para entrenamiento y los conjuntos de datos que vemos en producción. Adicionalmente agregaremos una columna para indicar a que set de datos pertenece.
[8]:
all_observations = np.concatenate([X, X_train], axis=0)
all_labels = np.concatenate([np.zeros(shape=(X.shape[0],)),
np.ones(shape=(X_train.shape[0],))], axis=0)
[52]:
import lightgbm as lgb
params = {
'objective': 'binary',
'max_depth': 100,
'num_leaves': 900,
'feature_fraction': 0.2,
'bagging_fraction': 0.8,
'boosting': 'gbdt',
'verbose': -1
}
lgb_data = lgb.Dataset(all_observations, label=all_labels)
gbm = lgb.train(params, lgb_data, num_boost_round=200)
[68]:
from sklearn.metrics import matthews_corrcoef, confusion_matrix
y_pred = gbm.predict(all_observations).round(0)
errors = np.count_nonzero(all_labels - y_pred)
successes = len(all_labels) - errors
[106]:
from scipy import stats
stats.binom_test(successes, n=len(all_labels), p=len(X_train)/len(all_labels), alternative='greater')
[106]:
0.5270730525677394
La hipotesis nula de que ambos conjuntos de datos son igual es rechazada en favor de la alternativa, con un nivel de significancia de 5% ya que el p-value retornado es mas pequeño que el valor critico de 5%.
Nota: En general, este tipo de pruebas debe realizarse sobre multiples muestreos de los conjuntos de datos para obtener un test más robusto. Estos pasos se han omitido en este ejemplo por simplicidad, pero recomendamos la lectura de: arXiv:1810.11953
Implicancias
Por diseño, esta técnica solo puede ser utilizada para detectar desviaciones en los predictores y por lo tanto no es una técnica apropiada para detectar desviaciones en la variable objetivo.
Adicionalmente, esta técnica requiere ejecutar este procedimiento cada vez que un nuevo lote de datos está disponible. Sin embargo, tiene una ventaja muy importante y es que permite analizar cuales son las observaciones que más difieren del set de datos de origen. Entonces, podemos obtener mayor información de qué es lo que está sucediendo con los datos o que suposiciones incorrectas hemos realizado. Es especialmente útil cuando solo algunos de los predictores ha cambiado.
Abrir en Google Colab
Descargar notebook