English Русский Deutsch 日本語 Português
preview
El problema del desacuerdo: profundizando en la explicabilidad de la complejidad en la IA

El problema del desacuerdo: profundizando en la explicabilidad de la complejidad en la IA

MetaTrader 5Aprendizaje automático | 3 septiembre 2024, 16:37
91 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

El problema del desacuerdo

El desacuerdo es un área de investigación clave dentro de una esfera interdisciplinar conocida como inteligencia artificial explicable (Explainable Artificial Intelligence, XAI). La inteligencia artificial explicable intenta ayudarnos a entender cómo nuestros modelos llegan a sus decisiones. Desafortunadamente, en la práctica resulta más complicado que eso. 

Todos sabemos que los modelos de aprendizaje automático y los conjuntos de datos disponibles son cada vez mayores y más complejos. De hecho, los científicos de datos que desarrollan algoritmos de aprendizaje automático no pueden explicar de manera precisa el comportamiento de sus algoritmos en todos los conjuntos de datos posibles. La Inteligencia Artificial Explicable (XAI) debe ayudarnos a explicar la funcionalidad de los modelos y confirmar que estos están listos para su uso práctico. Por prometedor que suene, este artículo mostrará al lector por qué no podemos confiar ciegamente en ninguna explicación que podamos obtener de cualquier aplicación de la tecnología de inteligencia artificial explicable. 

Contenido

  1. Introducción
  2. Resumen de los métodos de explicabilidad
  3. Explicaciones globales y locales
  4. Explicaciones agnósticas del modelo y explicaciones específicas del modelo
  5. Identificación del problema del desacuerdo y los factores que contribuyen a él
  6. Ejemplos
  7. Conclusión
  8. Recomendaciones


Introducción

El aprendizaje automático nos permite aprender las relaciones e interacciones que existen en los datos. Pero, ¿cómo podemos en este caso explorar las relaciones e interacciones que existen dentro del propio modelo? La mejor manera de responder a esta pregunta es usando técnicas de explicación de modelos. En este artículo, analizaremos algunos métodos diferentes de explicación. Los métodos de explicación basados en modelos pueden responder a preguntas como:

  • ¿Qué características han resultado más informativas para nuestro modelo?
  • Si hay 1 000 características en nuestro conjunto de datos, ¿cómo identificaremos las más y las menos informativas?
  • ¿Cómo cambia la información generada por nuestro modelo cuando se modifica una característica?
  • ¿Qué características merecerían un diseño más detallado?

Responder a estas preguntas es esencial, pero existe un gran obstáculo para hacerlo. El problema es que en ocasiones nos encontramos con desacuerdos entre las explicaciones que evalúan el mismo modelo. Desgraciadamente, al momento de escribir estas líneas, no existen soluciones mundialmente reconocidas para este problema, pero hoy intentaremos crear un sistema propio que mitigue la cuestión de alguna manera.

Existe una tendencia mundial a integrar la inteligencia artificial en un espectro de aplicaciones cada vez más amplio. Sin embargo, para confiar en los modelos, tenemos que poder explicar con detalle los propios modelos, las asociaciones aprendidas y el proceso de toma de decisiones. Esto se conoce por "explicabilidad". La Inteligencia Artificial Explicable (XAI) es capaz de monitorear cómo un modelo concreto logra sus predicciones. La IA explicable ha surgido como posible solución a un problema: conforme nuestras técnicas de modelización se vuelven más sofisticadas, nuestra capacidad para interpretarlas disminuye gradualmente. 

Empezaremos con un ejemplo sencillo, utilizando conceptos familiares para la mayoría de los lectores, así como una tarea bastante sencilla para desarrollar gradualmente la intuición sobre los distintos métodos de explicación. Tras analizar este ejemplo, deberíamos estar preparados para aplicar los conocimientos a cualquier modelo de aprendizaje automático entrenado con datos reales de mercado que recibamos del terminal MetaTrader 5.

El ejemplo sencillo con el que comenzaremos será una tarea simple: estimar el salario de un atleta dada su capacidad física. Para nosotros resulta obvio que, a medida que los deportistas desarrollan sus capacidades físicas, pueden esperar que se les pague más, pero la pregunta es: ¿qué capacidades físicas tienen mayor repercusión en los aumentos salariales?

En nuestro ejemplo, usaremos el muestreo de datos recogidos por el titán de los videojuegos Electronic Arts como parte de su serie de videojuegos Madden NFL, en la que los jugadores pueden simular partidos jugando como su jugador profesional favorito de fútbol americano. El conjunto de datos contiene estadísticas detalladas sobre jugadores profesionales de fútbol americano. Entrenaremos cuatro modelos diferentes para predecir el salario de un jugador basándonos en sus características, como la edad, la velocidad de sprint y la fuerza. A continuación, aplicaremos diferentes métodos para explicar los modelos y ver qué información podemos obtener sobre la relación entre las características de un jugador y su salario. Así, veremos cómo se interpreta cada método de explicación y, a continuación, comprobaremos si hay explicaciones contradictorias. Luego destacaremos los desacuerdos, trataremos de identificar lo que puede estar contribuyendo al estos y debatiremos posibles soluciones. 


Serie de videojuegos "Madden NFL" de EA

Figura 1. Videojuego Madden NFL de Electronic Arts.


Resumen de los métodos de explicabilidad

En general, los métodos de explicabilidad pueden clasificarse de distintas formas. Lo más fácil es dividirlos en dos clases: explicadores de caja blanca y explicadores de caja negra. En un artículo anterior ya hablamos de los modelos de caja de cristal (blanca) y caja negra. Hoy no nos interesan los modelos predictivos, así que la caja negra de la que hablaremos no supondrá un modelo de aprendizaje automático complejo y difícil de interpretar. Ahora se trata de algoritmos explicativos que ayudarán a interpretar el modelo básico.

La técnica de explicación de la caja negra está diseñada para su uso en cualquier tipo de modelo. Este tipo también se conoce como explicadores agnósticos de modelos.

La técnica de explicación de la caja blanca está diseñada para usar la estructura de un tipo concreto de modelo básico con el fin de ofrecer explicaciones que puedan resultar más precisas para el modelo básico.

Es de esperar que los diferentes métodos de explicación:

  • Hagan diferentes suposiciones sobre la forma y la estructura de los datos básicos.
  • Identifiquen y evalúen diversos indicadores para comprender el modelo.
  • No puedan hacer frente a determinadas circunstancias.

Los algoritmos de explicabilidad también pueden clasificarse según su metodología. Por ejemplo, existen métodos de explicación que obtienen información ajustando las entradas de cada característica de una en una y observando los cambios subsiguientes en las predicciones. Estos métodos pueden clasificarse como métodos basados en perturbaciones. Por otra parte, existen algunos algoritmos explicativos que tratan de entender hasta qué punto es sensible el modelo a los cambios en las características. Estos métodos pueden clasificarse como métodos basados en el gradiente, pues toman la derivada de los datos de salida del modelo en función de sus características.

Hoy examinaremos solo algunos tipos de algoritmos explicativos. Existen muchos otros métodos explicativos que no hemos tratado, por lo que esta lista no pretende ser exhaustiva.

Por último, también debemos gestionar nuestras expectativas. Crear un modelo capaz de predecir el precio de un valor con una precisión superior al 50% no garantiza necesariamente un comercio rentable. 


Explicaciones globales y locales

Si necesitamos saber más sobre la importancia de las características y el comportamiento general de nuestro modelo, deberemos utilizar técnicas de explicación global. Por el contrario, si queremos comprender con detalle cómo nuestro modelo ha llegado a una predicción concreta, necesitaremos explicaciones localizadas. 

Las explicaciones locales nos permitirán comprender la relación entre características a partir de la cual podremos confiar mejor en las predicciones de nuestro modelo. Además, también podremos comprender mejor qué características contribuyen a las predicciones falsas, de manera que podamos plantearnos desarrollarlas para extraer de ellas más información útil.

Volvamos a nuestro ejemplo simple. Para averiguar qué características son importantes a la hora de predecir el salario de un deportista, deberemos aplicar métodos de explicación global como la importancia de las permutaciones. Además, si necesitamos comprender con más detalle la predicción de un atleta concreto, serán necesarios métodos de explicación localizada como LIME. 


En nuestro ejemplo, primero analizaremos las explicaciones globales de nuestro modelo.

Como siempre, empezaremos estableciendo las dependencias que necesitamos.

Ajustaremos:

  • Shap
  • eli5
  • Cal
  • Interpret
  • alibi

pip install alibi shap lime eli5 interpret

A continuación, cargaremos nuestras dependencias habituales.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

Ahora podemos leer el conjunto de datos.

csv = pd.read_csv("/add/your/path/here/to/the/madden/csv")

Veamos algunas filas y columnas del conjunto de datos.

Conjunto de datos Madden

Figura 2. Ejemplo de conjunto de datos. 

Para simplificar, mantendremos solo algunas características numéricas. Los características categóricos requieren métodos y detalles específicos que quedan fuera del ámbito de nuestra discusión.

predictors = ["awareness_rating","throwPower_rating","kickReturn_rating",
              "leadBlock_rating","strength_rating","catchInTraffic_rating",
              "pursuit_rating", "catching_rating","acceleration_rating",
              "height","tackle_rating","yearsPro","throwUnderPressure_rating",
              "throwAccuracyDeep_rating","throwAccuracyShort_rating","speed_rating",
              "jumping_rating","toughness_rating","kickPower_rating",
              "kickAccuracy_rating","agility_rating","passBlock_rating","age"
             ]

csv[predictors].dtypes

awareness_rating int64

throwPower_rating int64

kickReturn_rating int64

Ahora definiremos el objetivo.

target = "totalSalary"

Y configuraremos los modelos que vamos a utilizar.

from sklearn.linear_model import LinearRegression
from sklearn.neighbors import KNeighborsRegressor
from sklearn.ensemble import RandomForestRegressor
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.callbacks import EarlyStopping

Tras importar las dependencias necesarias y antes de inicializar nuestros modelos de aprendizaje automático, aplicaremos algunos pasos de preprocesamiento para escalar y normalizar los datos, y también para dividirlos en entrenamiento y pruebas. He omitido estos pasos en la descripción del artículo para no sobrecargarla con detalles innecesarios. Así, dividiremos los datos en datos de entrenamiento y datos de prueba, obteniendo las variables para los datos de entrenamiento x_train e y_train, las variables para los datos de prueba x_valid e y_valid" y las variables para las pruebas x_test e y_test.

Luego pasaremos a configurar cada uno de los modelos con los que trabajaremos hoy.

lm = LinearRegression()
rf = RandomForestRegressor()
knn = KNeighborsRegressor(n_neighbors=10)
dnn = keras.Sequential([
    layers.Dense(units=30,activation="relu",input_shape=[scaled_data.shape[1]]),
    layers.Dense(units=20,activation="relu"),
    layers.Dropout(0.3),
    layers.Dense(units=10,activation="relu"),
    layers.Dense(units=1)
])

A continuación, entrenaremos cada uno de los modelos

#Fitting the linear regression
lm.fit(x_train,y_train)

#Fitting the k-nearest neighbor regressor
knn.fit(x_train,y_train)

#Fitting the random forest model
rf.fit(x_train,y_train)

#Fitting the deep neural network
early_stopping = EarlyStopping(
    min_delta=0.001,
    patience=20,
    restore_best_weights=True
)

dnn.compile(optimizer="adam",loss="mae")

dnn.fit(
    x_train,y_train,
    validation_data=(x_validation,y_validation),
    batch_size = 60,
    epochs=100,
    verbose=0,
    callbacks=[early_stopping]
)

Explicaciones globales

Comenzaremos con una explicación global de nuestro modelo de regresión lineal. La técnica que usaremos es la importancia de las permutaciones. Se trata de un método global para explicar la caja negra.

import eli5
from sklearn.metrics import mean_squared_error
from eli5.sklearn import PermutationImportance
from eli5.permutation_importance import get_score_importances

Después obtendremos explicaciones globales del modelo de regresión lineal

permutation = PermutationImportance(lm).fit(x_test,y_test)
eli5.show_weights(permutation,feature_names = predictors)

Explicaciones globales de nuestro modelo de regresión lineal

Figura 3. Explicaciones globales de nuestro modelo de regresión lineal.

Ahora pasaremos a la interpretación de los resultados. La importancia de la permutación supone que cada columna del conjunto de datos será independiente. Por lo tanto, podremos cambiar una columna cada vez para observar el cambio en las tasas de error del modelo y utilizarlas para decidir qué características son importantes. Si una característica ha sido importante, el hecho de barajar sus valores aumentará los porcentajes de error del modelo y, a la inversa, si una característica no ha sido importante, los porcentajes de error del modelo disminuirán tras barajar los valores de esa columna. Cuando el supuesto de independencia no es cierto, deberemos interpretar estos resultados con cierto escepticismo.

En la tabla anterior, las características importantes tienen coeficientes positivos y las no importantes, coeficientes negativos. yearsPro, los años de experiencia del atleta, tiene un coeficiente negativo, pero sabemos lo que realmente importa: ¿ha detectado nuestro modelo un fenómeno que desconocemos, o nuestro explicador no nos está dando una representación exacta de la verdad? 

La importancia de la permutación puede barajar de forma poco realista la experiencia de un deportista, haciéndolo potencialmente demasiado joven para su nivel o con más experiencia de lo que implica su edad total. Además, este método ignorará cualquier interacción que pueda haber en el conjunto de datos y atribuirá todos los cambios en el error del modelo solo a ese objeto. Por consiguiente, los resultados podrían ser poco realistas. Esto vuelve a destacar la importancia de tener cuidado y entender bien el algoritmo, en lugar de aplicarlo a ciegas.

La duración del recorrido, a su vez, sí que puede darnos información valiosa sobre las características más importantes del modelo. En este caso, la capacidad del atleta para atrapar el balón y su agilidad son algunos de los características más importantes, y esto tiene sentido porque en el fútbol se espera que el jugador sea capaz de atrapar pases amplios de sus compañeros, aunque esté sometido a una fuerte presión. Además, una vez que coge el balón, tiene que alejarse del mayor número posible de rivales sin perderlo, por lo que la importancia que se da a estas señales parece ser cierta.


Explicaciones locales

Como hemos dicho antes, las explicaciones locales nos ayudarán a entender cómo influye cada característica en cada predicción individual que hace nuestro modelo.

Así, obtendremos explicaciones locales de las expectativas de nuestra red neuronal profunda sobre el salario de Richard LeCounte.


Richard LeCounte

Figura 4: Richard LeCounteIII

En primer lugar, importaremos LIME.

LIME es el acrónimo de Locally Interpretable Model-Agnostic Explanations (explicaciones agnósticas de modelos localmente interpretables). El método explicará qué características han influido en los resultados del modelo y si estos factores han afectado a los resultados para bien o para mal. 

import lime
from lime import lime_tabular
explainer = lime_tabular.LimeTabularExplainer(training_data = np.array(x_train),mode='regression',feature_names=predictors)
exp = explainer.explain_instance(data_row=scaled_data.iloc[test],predict_fn=lm.predict)
exp.show_in_notebook(show_table=True)



Explicaciones de la web profunda de LIM

Figura 5. Explicaciones de LIME para el salario esperado de nuestra red neuronal profunda sobre el jugador Richard LeCounte


Los gráficos contienen una gran cantidad de información: en primer lugar, podemos ver cuáles de las habilidades físicas de LeCounte aumentan su salario esperado y cuáles lo disminuyen. Nuestra red neuronal esperaba que el índice de resiliencia de LeCounte redujera su salario esperado, lo cual tiene sentido ya que LeCounte tiene un índice de resiliencia medio. Además, la red neuronal profunda identificó correctamente los puntos fuertes de LeCounte y esperaba que su salario aumentara gracias a sus puntos fuertes. Este es exactamente el comportamiento que cabría esperar. Las explicaciones locales nos permiten poner a prueba el modelo en lugar de confiar ciegamente en él.


Explicaciones agnósticas del modelo y explicaciones específicas del modelo

En los casos en los que conocemos la estructura del modelo básico, podemos usar métodos de explicación diseñados específicamente para esa arquitectura del modelo. Se suelen denominar explicadores de caja blanca. En la práctica, los explicadores de caja blanca podrían ser computacionalmente más eficientes que sus homólogos de caja negra.

Los explicadores de caja negra son independientes del modelo, es decir, pueden interpretar y explicar cualquier modelo que les presentemos. El algoritmo LIME que acabamos de analizar supone un ejemplo de caja negra explicativa, como su nombre indica: Locally Interpretable Model-Agnostic Explanations (explicaciones independientes del modelo localmente interpretables). 

Las explicaciones de caja blanca pueden usarse cuando conocemos exactamente la estructura del modelo básico. Al hacerlo, ofrecerán explicaciones más precisas del comportamiento de nuestro modelo. Dicho esto, los explicadores de caja negra suelen introducir cierto nivel de desplazamiento al explicar nuestros modelos. Los explicadores de la caja negra realizan algunas suposiciones simplificadoras sobre la forma del modelo con el que están tratando. Y si esas suposiciones no se cumplen, obtendremos las explicaciones equivocadas.

Como regla general, si conocemos con exactitud las propiedades de un modelo, será mejor utilizar explicadores de caja blanca. 

Echemos un vistazo al explicador de caja negra en acción. Existen implementaciones de los explicadores de caja negra SHAP en la biblioteca SHAP. Veamos una de estas. 

Importación SHAP

Импорт SHAP

Cálculo de los valores SHAP

explainer = shap.Explainer(xgb.predict,scaled_data.loc[test:test_end,predictors])
shap_values = explainer(scaled_data.loc[test:test_end,predictors])

Construcción de una explicación de la caja negra

shap.plots.beeswarm(shap_values)


Explicaciones de la caja negra SHAP

Figura 6. Explicaciones de la caja negra SHAP para nuestro modelo XGB


La anchura de cada fila vendrá determinada por los valores atípicos y no aportará ninguna información sobre la importancia del objeto. Estimaremos la importancia de un objeto teniendo en cuenta su posición vertical en el gráfico. Cuanto más alto se sitúa un objeto, más importante lo percibirá el algoritmo SHAP.

Observe también que en algunos objetos los puntos azules y rosas están bien separados, mientras que en otras filas están mezclados. La mezcla de los puntos suele ser una indicación de un efecto de interacción entre esa característica y otra característica o conjunto de características. Y toda esta cantidad de información se mostrará en un único gráfico SHAP. 

Cada punto representará el valor de Shepley para cada punto de datos. Esto nos permitirá evaluar rápidamente cómo cada punto de datos individual ha afectado a la predicción del modelo a un nivel detallado. El color del punto representará la dirección y la magnitud de la influencia del punto de datos en la predicción de nuestro modelo. El azul indicará una disminución de los resultados del modelo, mientras que el rosa indicará un aumento. Tenga en cuenta que el algoritmo SHAP presupone que los objetos son independientes o, en otras palabras, ignorará cualquier interacción en el conjunto de datos. Si esta suposición no se cumple, el algoritmo no será fiable. 

Vamos a analizar ahora un explicador de caja blanca diseñado para modelos de árbol como nuestro modelo XGB. Utilizaremos un método especial TreeExplainer() diseñado para explicar modelos de árbol.

tree_explainer = shap.TreeExplainer(xgb)
tree_shap_values = explainer(x_test)

A continuación, podremos construir explicaciones de caja blanca.

shap.plots.beeswarm(tree_shap_values)

Explicación de la caja blanca SHAP

Figura 7. Explicación de la caja blanca SHAP para nuestro modelo XGB

Compararemos las 4 características más importantes de la explicación SHAP para la caja negra y la caja blanca. ¿Ve el problema del desacuerdo? 


Identificación del problema del desacuerdo y los factores que contribuyen a él

Así pues, ya nos hemos familiarizado con los modelos explicativos. A continuación, pasaremos directamente al problema del desacuerdo. Empezaremos por considerar qué es un desacuerdo.
Ahora veremos en qué consiste el desacuerdo en el ámbito de las explicaciones sobre la importancia de las características:
  • Permutaciones de funciones. La primera forma de desacuerdo se dará cuando las permutaciones de funciones difieran. Si en nuestro flujo de trabajo importa un determinado orden de importancia de las funciones, la aparición de diferentes permutaciones de explicaciones se convertirá en un punto clave de conflicto. Esto resulta especialmente importante cuando la secuencia detallada de funciones es relevante para el análisis.
  • Conjuntos de características mejor valorados. Si profundizamos más, el desacuerdo se extenderá a los conjuntos de características mejor valorados. Cuando el cumplimiento estricto de la permutación precisa de la importancia de las funciones se debilita, la atención se desplazará al conjunto de funciones principales. Una discrepancia en las 3, 5 o n primeras características identificadas por diferentes explicadores para el mismo modelo se convertirá en un punto de divergencia notable.
  • Variación del signo del coeficiente. El rendimiento de muchos explicadores dependerá de los coeficientes asignados a las características, que indicarán su contribución positiva o negativa al resultado deseado. A veces resulta posible observar un cambio de signo de este coeficiente entre dos explicaciones del mismo modelo. Estos cambios sirven como indicadores de desacuerdo. En ese caso, deberemos examinar más detenidamente el funcionamiento interno del modelo.
  • Ordenación relativa de las características de interés. Al examinar más de cerca las características específicas de interés, el desacuerdo adquiere otra dimensión. Si nos centramos en comparar la importancia de una función frente a otra, los cambios en el orden de las dos funciones en las distintas explicaciones se convertirán en motivo de desacuerdo.

¿Qué no es un desacuerdo?

Es importante considerar que no todas las diferencias significan desacuerdo. Vamos a considerar casos como este:

  • Valores de importancia divergentes. Hay veces en que los valores de importancia de las características calculados por distintos explicadores no coinciden con exactitud. Esto no es un desacuerdo. La cuestión es que los distintos algoritmos dan cálculos distintos, lo cual dificulta la comparación directa. En este caso, no se hace hincapié en la igualdad numérica de los valores de importancia, sino en la coherencia de las conclusiones derivadas de las distintas explicaciones.
  • Comprensión coherente de valores precisos. La esencia de un análisis correcto será la búsqueda de una comprensión coherente, no la búsqueda de una igualdad numérica exacta. No podemos esperar que los explicadores ofrezcan una estimación muy precisa con valores concretos. Aquí deberemos adoptar una visión más amplia y darnos cuenta de que las explicaciones, aunque difieran numéricamente, pueden converger, por ejemplo, en la dirección.
  • Complejidad del modelo y problemas de estimación. A medida que aumenta la complejidad del modelo, la dificultad de valorar con precisión su funcionamiento interno aumentará en proporción directa a la complejidad del modelo. Solo tendremos que reconocer esa complejidad intrínseca. Por lo tanto, cuando se trata de modelos muy complejos, la divergencia en los valores de los explicadores se convertirá en un problema esperado más que en un motivo de alarma. Esta opinión resulta coherente con la naturaleza evolutiva de la IA, donde la búsqueda de transparencia se enfrenta a complejidades cada vez mayores.

¿Por qué discrepan entre sí los explicadores?
Existen muchas razones por las que los explicadores pueden no estar de acuerdo entre sí; intentaremos resolver juntos algunos casos.

Comparación de los explicadores globales y locales

Una de las razones por las que podemos ver desacuerdos es porque podemos comparar explicaciones globales y locales. Aunque esta sea la explicación obvia, uno quiere asegurarse de no caer nunca en este error. Las explicaciones globales y locales no pueden compararse directamente entre sí. A menos que tengamos un conocimiento detallado de cómo se implementan los algoritmos, comparar las explicaciones globales y locales resulta inapropiado, ya que probablemente conducirá a explicaciones diferentes.

Comparación de los explicadores de caja negra y caja blanca

Lo que es más, no podemos comparar directamente las explicaciones de caja negra y caja blanca. Recuerde que los explicadores de cajas negras intentan mostrar la utilidad la capacidad de explicar cualquier algoritmo, por lo que hacen ciertas suposiciones simplificadoras sobre el modelo o su comportamiento. Estas suposiciones permiten que los explicadores de cajas negras funcionen en una amplia gama de circunstancias, pero no siempre están justificadas y las suposiciones pueden resultar demasiado grandes. Como consecuencia, las explicaciones de la caja negra y la caja blanca serán diferentes. 

Cambio en el orden de las funciones

Cualquier cambio en el orden de presentación de las características puede ser una causa inesperada de desacuerdo. Algunos métodos de explicación resultan sensibles al orden de las entradas del modelo, otros no. Por ejemplo, el método de la información mutua no es sensible al orden de entrada, a diferencia del SHAP. Como consecuencia, es posible que el explicador SHAP no pueda estar de acuerdo consigo mismo tras observar patrones idénticos.  

Por ejemplo, más abajo le mostramos dos explicaciones de SHAP derivadas de dos XGBRegressors idénticos. La única diferencia entre los dos modelos es el orden de las características que toman como entrada. De este modo, se entrenarán con el mismo conjunto de datos y tendrán el mismo objetivo. 


Valores SHAP iniciales

Figura 8. Valores SHAP iniciales


Valores barajados y SHAP

Figura 9. Valores SHAP después de cambiar el orden de los elementos.

Al cambiar el orden de las funciones en el modelo, veremos que nuestras explicaciones son ahora incoherentes entre sí. Aunque no tenemos ninguna información nueva, la característica de altura ha subido de repente. Los tres primeros puestos se han mantenido, pero la mayoría de las características han subido y bajado, cambiando de posición. Si tiene previsto utilizar técnicas de bootstrapping u otras formas de remuestreo o selección de subconjuntos, deberá estar preparado para este comportamiento. Se trata de un fenómeno normal y esperado. 


Además, distintas aplicaciones de la misma técnica de explicación podrían no coincidir entre sí debido a pequeñas diferencias en sus cálculos. Por ejemplo, la implementación de la importancia de la permutación en eli5 resulta sensible al orden de los datos de entrada, mientras que la implementación de la misma técnica en sklearn no lo es, porque una misma técnica de explicación puede realizarse de distintas maneras. 

Para demostrar que la implementación de eli5 de la importancia de las permutaciones resulta sensible al orden de los datos de entrada, a continuación les mostramos dos explicaciones obtenidas utilizando la implementación de eli5 de la importancia de las permutaciones. Hemos obtenido resultados de dos XGBRegressors idénticos. La única diferencia entre los dos modelos es el orden de las características que toman como entrada. De este modo, se entrenarán con el mismo conjunto de datos y tendrán el mismo objetivo. 


Importancia inicial de la permutación

Figura 10. Importancia inicial de la permutación


Importancia barajada de la permutación

Figura 11. Importancia barajada de la permutación


Observe que el parámetro kickPower_rating tenía originalmente un coeficiente positivo, ¡pero tras cambiar el orden de las funciones ahora tiene un coeficiente negativo! Estas fluctuaciones inestables se consideran un fuerte desacuerdo. Los características más importantes han cambiado, al igual que el orden relativo entre características.

Por último, uno de los puntos más evidentes sería la divergencia metodológica. Esta da lugar al interesante fenómeno de que los explicadores discrepen entre sí, aunque ambos expliquen el mismo modelo. Como cada explicador calcula diferentes medidas del modelo, cada uno podrá interpretar el modelo de forma diferente.

Afortunadamente, XGB cuenta con una función que permite valorar la importancia de las características. Vamos a usar esta función y ver qué método de explicación ofrece una representación válida de la verdad. También hemos añadido directamente aquí los resultados de las explicaciones anteriores para que no tengamos que desplazarnos arriba y abajo.


Valores XGB verdaderos

Figura 12. Importancia de las características XGB

Importancia de la permutación XGB

Figura 13. Importancia de la permutación XGB


Explicaciones de la caja blanca SHAP

Figura 14: Explicaciones de la caja blanca SHAP


Explicaciones de la caja negra de SHAP

Figura 15. Explicaciones de la caja negra SHAP


Explainable Boosting Machine (EBM)

Figura 16. Importancia de la función del modelo basado en un árbol, caja de cristal

El índice de concienciación era la característica más importante del modelo, pero el pequeño conjunto de métodos de explicación de caja negra no lograba explicarlo, y no lograba gestionar en absoluto las tres características principales. Este es otro recordatorio de que las explicaciones son meras estimaciones de la importancia de las características, no verdades absolutas. También debemos considerar que, aunque yearsPro ha ocupado repetidamente los primeros puestos, en realidad no es tan importante. Es decir, no deberemos confiar en el hecho de que las características que ocupan un lugar destacado varias veces en diferentes pruebas sean realmente importantes de forma inherente. 

La explicación de la caja negra que hemos aplicado a nuestro modelo XGB contradice la explicación de la caja blanca, pero al final, ambas explicaciones han resultado ser erróneas. Averiguar cuál de las explicaciones resulta más precisa sería una pérdida de tiempo, porque ninguna de ellas nos acerca a la verdad.

También hemos ejecutado ema en los datos para ver si el modelo de caja de cristal podría ser utilizado como un proxy para la importancia de la característica. Aunque ha sido capaz de identificar correctamente la característica más importante del conjunto de datos, esto no siempre es una opción, especialmente si nuestro modelo no está basado en árboles. Desafortunadamente, el modelo de la caja de cristal también ha encontrado que la característica yearsPro es informativa, aunque no lo es. Esto significa que los explicadores podrán, entre otras cosas, ¡aceptar información falsa!


Ejemplos

Ahora estamos listos para empezar a trabajar con datos reales del mercado. En primer lugar, obtendremos los datos del terminal MetaTrader 5. Para lograr una extracción y conversión de datos eficiente, desarrollaremos un script en MQL5. Empezaremos con la declaración de variables globales.
//---Our handlers for our indicators
int ma_handle;
int rsi_handle;
int cci_handle;
int ao_handle;
int bbands_handle;
int atr_handle;

A continuación, crearemos arrays para almacenar los valores de los indicadores. Aquí recogeremos metódicamente y luego utilizaremos los resultados de los indicadores a lo largo de la ejecución del script. 

//---Data structures to store the readings from our indicators
double ma_reading[];
double rsi_reading[];
double cci_reading[];
double ao_reading[];
double bb_high_reading[];
double bb_low_reading[];
double bb_mid_reading[];
double atr_reading[];

Así, crearemos un archivo CSV. 

//---File name
string file_name = "The Dissagrement Problem Data.csv";

A continuación, crearemos una variable para almacenar el número de barras a solicitar.

//---Amount of data requested
int size = 10000;
int size_fetch = size + 50;

Luego pasaremos a configurar los manejadores de los indicadores. Para configurar el trabajo con el indicador, deberemos especificar el símbolo y el marco temporal en el que lo utilizaremos. Después, dependiendo del indicador, puede que tengamos que especificar el periodo del indicador, el parámetro de desplazamiento, el método de suavizado y el tipo de precio que vamos a calcular. 

void OnStart()
  {
      //---Setup our technical indicators
      ma_handle = iMA(_Symbol,PERIOD_CURRENT,20,0,MODE_EMA,PRICE_CLOSE);
      rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,60,PRICE_CLOSE);
      cci_handle = iCCI(_Symbol,PERIOD_CURRENT,10,PRICE_CLOSE);
      ao_handle = iAO(_Symbol,PERIOD_CURRENT);
      bbands_handle = iBands(_Symbol,PERIOD_CURRENT,120,0,0.2,PRICE_CLOSE);
      atr_handle = iATR(_Symbol,PERIOD_CURRENT,14);

Luego tendremos que transferir los valores de los indicadores a las estructuras de datos.

 //---Set the values as series
      CopyBuffer(ma_handle,0,0,size_fetch,ma_reading);
      ArraySetAsSeries(ma_reading,true);
      CopyBuffer(rsi_handle,0,0,size_fetch,rsi_reading);
      ArraySetAsSeries(rsi_reading,true);
      CopyBuffer(cci_handle,0,0,size_fetch,cci_reading);
      ArraySetAsSeries(cci_reading,true);
      CopyBuffer(ao_handle,0,0,size_fetch,ao_reading);
      ArraySetAsSeries(ao_reading,true);
      CopyBuffer(bbands_handle,0,0,size_fetch,bb_mid_reading);
      ArraySetAsSeries(bb_mid_reading,true);
      CopyBuffer(bbands_handle,1,0,size_fetch,bb_high_reading);
      ArraySetAsSeries(bb_high_reading,true);
      CopyBuffer(bbands_handle,2,0,size_fetch,bb_low_reading);
      ArraySetAsSeries(bb_low_reading,true);
      CopyBuffer(atr_handle,0,0,size_fetch,atr_reading);
      ArraySetAsSeries(atr_reading,true);

Antes de comenzar el proceso de escritura de los archivos, deberemos incluir un manejador de archivos en el script. Estos ajustes nos permitirán crear y trabajar correctamente con el archivo de salida.

      //---Write to file
       int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");

El siguiente será un paso importante en nuestro script: deberemos iterar por los arrays de precios históricos y valores de indicadores para escribir los datos necesarios en el archivo CSV.

for(int i=-1;i<=size;i++){
      if(i == -1){
            FileWrite(file_handle,"Time","Open","High","Low","Close","MA 20","RSI 60","CCI 10","AO","BBANDS 120 MID","BBANDS 120 HIGH","BBANDS 120 LOW","ATR 14");
      }
      
      else{
            FileWrite(file_handle,iTime(_Symbol,PERIOD_CURRENT,i),
                                 iOpen(_Symbol,PERIOD_CURRENT,i),
                                 iHigh(_Symbol,PERIOD_CURRENT,i),
                                 iLow(_Symbol,PERIOD_CURRENT,i),
                                 iClose(_Symbol,PERIOD_CURRENT,i),
                                 ma_reading[i],
                                 rsi_reading[i],
                                 cci_reading[i],
                                 ao_reading[i],
                                 bb_mid_reading[i],
                                 bb_high_reading[i],
                                 bb_low_reading[i],
                                 atr_reading[i]);
      }
    }
    FileClose(file_handle);
  }


Datos de mercado

Figura 17. Datos de mercado del terminal MetaTrader 5.


Ahora estamos listos para procesar los datos y poner a prueba lo que hemos aprendido del ejemplo de la muestra. 

Primero importaremos las dependencias.

#Import Dependencies
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import shap
import lime
from lime import lime_tabular
import eli5
from eli5.sklearn import PermutationImportance
from sklearn.feature_selection import mutual_info_regression

Luego descargaremos las dependencias para IntepretML.

from interpret import set_visualize_provider
from interpret.provider import InlineProvider
set_visualize_provider(InlineProvider())
from interpret import show
from interpret.blackbox import MorrisSensitivity

Y leeremos el csv.

csv = pd.read_csv("/enter/your/path/here")

Después prepararemos el objetivo.

csv["Target"] = csv["Close"].shift(-30)

Descartaremos todas las filas con valores omitidos.

csv.dropna(axis=0,inplace=True)

Crearemos una lista de predictores.

drop = ["Time","Target"]
predictors = csv.columns.tolist()
predictors = [col for col in predictors if col not in drop]
predictors

Y escalaremos los datos.

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
print(scaler.fit(csv.loc[:,predictors]))
scaled_data =  pd.DataFrame(scaler.transform(csv.loc[:,predictors]), index = csv.index, columns = predictors)
scaled_data


Escalado de datos de mercado

Figura 18. Escalado de datos de mercado

Configuración de los algoritmos de caja negra

#Black box models
from xgboost import XGBRegressor
from sklearn.linear_model import LinearRegression
from xgboost import plot_importance
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.callbacks import EarlyStopping

Separación de los datos en muestra de entrenamiento y muestra de prueba

train = 0
train_end = 5000
test = train_end + 40

Configuración del modelo lineal

lm = LinearRegression()
lm.fit(scaled_data.loc[train:train_end,predictors],csv.loc[train:train_end,"Target"])

Personalización del modelo XGB

#XGBModel
xgb = XGBRegressor()
xgb.fit(scaled_data.loc[train:train_end,predictors],csv.loc[train:train_end,"Target"])

Configuración de un modelo de red neuronal profunda

dnn = keras.Sequential([
    layers.Dense(units=30,activation="relu",input_shape=[scaled_data.shape[1]]),
    layers.Dense(units=20,activation="relu"),
    layers.Dropout(0.3),
    layers.Dense(units=10,activation="relu"),
    layers.Dense(units=1)
])
early_stopping = EarlyStopping(
    min_delta=0.001,
    patience=20,
    restore_best_weights=True
)
dnn.compile(optimizer="adam",loss="mae")
dnn.fit(
    scaled_data.loc[train:train_end,predictors],csv.loc[train:train_end,"Target"],
    validation_data=(csv.loc[validation:validation_end,predictors],csv.loc[validation:validation_end,"Target"]),
    batch_size = 60,
    epochs=100,
    verbose=0,
    callbacks=[early_stopping]
)


Obtención de explicaciones globales para una red neuronal profunda

Para obtener una explicación de nuestra red profunda, usaremos el paquete alibi. El paquete alibi cuenta con una útil implementación de la técnica explicativa «Efectos Locales Acumulados» (Accumulated Local Effects, ALE). El método ALE es bastante robusto y puede procesar características altamente correlacionadas. No hace suposiciones explícitas sobre la independencia de los características, puede interpretarse visualmente, es eficiente desde el punto de vista computacional y, a diferencia de otros métodos explicativos, no se basa en suposiciones de linealidad, lo cual lo hace muy adecuado para identificar relaciones complejas en los datos.   

En primer lugar, calcularemos los valores ALE.

dnn_ale = ALE(dnn.predict, feature_names  = predictors,target_names=["Close 30 Steps"])

A continuación, convertiremos los datos de entrada en formato NumPy para poder trazar gráficos ALE.

X = scaled_data.loc[train:train_end,predictors].to_numpy()
dnn_alibi =dnn_ale.explain(X)
plot_ale(dnn_alibi,n_cols=4, fig_kw={'figwidth': 20, 'figheight': 10}, sharey=None)

Los gráficos ALE ayudan a interpretar cómo los cambios en cada característica afectan a los resultados del modelo. Por ejemplo, a continuación le mostramos el gráfico ALE de nuestro Awesome Oscillator. Como podemos ver, Awesome Oscillator puede ayudar a nuestra red neuronal profunda a anticipar cuándo caerá el precio. Vemos una pendiente descendente en el gráfico ALE a medida que aumenta el valor de Awesome Oscillator.

ALE AO

Figura 19. Gráfico ALE para Awesome Oscillator


Además, las características no informativas tendrán un gráfico ALE parecido a una línea horizontal, lo cual significa que los cambios en este signo tendrán poco efecto sobre el objetivo. En nuestro estudio, el índice RSI no ha sido informativo. 

ALE RSI

Figura 20. ALE RSI

Obtención de explicaciones globales partiendo de una explicación SHAP independiente del modelo

explainer = shap.Explainer(xgb.predict,scaled_data.loc[test:,predictors])
shap_values = explainer(scaled_data.loc[test:,predictors])
shap.plots.beeswarm(shap_values)


Gráfico de Beeswarm de caja negra SHAP para nuestro modelo XGB

Figura 21. Gráfico SHAP Black-box Beeswarm para XGB

El gráfico de beeswarm muestra que el Awesome Oscillator es la característica más importante en este conjunto de datos. Recuerde que la anchura de cada línea del gráfico no aportará ninguna información. Además, podemos observar que los puntos azules y rosas no están mezclados, lo cual significa que puede que no haya términos de interacción fuertes en el conjunto de datos.

Importancia de una característica según la información mutua (Mutual Information, IM)

mi_scores = mutual_info_regression(scaled_data.loc[test:,predictors], csv.loc[test:,"Target"])
mi_scores = pd.Series(mi_scores, name="MI Scores", index=scaled_data.columns)
mi_scores = mi_scores.sort_values(ascending=False)
mi_scores

BBANDS 120 MID     1.739039

BBANDS 120 HIGH    1.731220

BBANDS 120 LOW     1.716019

MA 20                      1.525800

High                        1.172096

Open                       1.155583

Close                       1.143642

Low                         1.140613

ATR 14                     0.421772

AO                           0.232608

RSI 60                      0.181932

CCI 10                     0.016491

El MI contradice completamente nuestra explicación de SHAP en lo que respecta a las cuatro características principales. Solo plantea más dudas sobre nuestro ya confuso caso. Peor aún, el explicador de SHAP tenía razón al decir que el Awesome Oscillator es la característica más importante en este caso. Así pues, el uso de explicadores múltiples supone en realidad una espada de doble filo: por un lado, protege contra el sesgo heredado de cualquier explicación utilizada, pero, por otro, crea más espacio para la probabilidad de desacuerdo. Resulta difícil decir qué caso es empíricamente mejor porque todo depende del conjunto de datos concreto, del modelo concreto y de muchas otras variables.

Por último, consideraremos el análisis de sensibilidad de Morris (Morris Sensitivity Analysis)

msa = MorrisSensitivity(xgb, scaled_data.loc[train:train_end,predictors])
show(msa.explain_global())


Análisis de sensibilidad de Morris

Figura 22: Análisis de sensibilidad de Morris

Puntuaciones de importancia reales de nuestro modelo XGB.

Datos XGB reales

Figura 23. Importancia de los datos de mercado XGB

La media móvil ha sido la característica más importante de la muestra, seguida de Awesome Oscillator. En este caso concreto, el análisis de sensibilidad de Morris ha ofrecido explicaciones relativamente mejores, pero en la mayoría de los casos no tenemos acceso a la verdad, así que ¿cómo elegiremos a qué explicación dar más peso? ¿Cómo podemos confiar en su decisión? ¿Cómo podremos comprobar las decisiones sobre la importancia de las características si no tenemos acceso a las verdaderas tablas de importancia de las mismas? 


Conclusión

Como vemos, no existen respuestas fáciles: hay demasiados componentes. Tenemos que considerar la forma y la estructura de los datos, el modelo básico, la presencia de términos de interacción en los datos y, sobre todo, el funcionamiento interno de cada explicador. Además, en el ejemplo controlado, podemos ver que todos los explicadores pueden estar equivocados, por lo que, en tal situación, intentar resolver cualquier desacuerdo sería una pérdida de tiempo. Así pues, el beneficio que obtendremos de los métodos explicativos no siempre justifica la complejidad que pueden introducir. Esperemos que con el tiempo podamos aplicar mejores métodos explicativos y algoritmos mejores.

Recomendaciones

Considerando todo esto, quizá una mejor solución al problema del desacuerdo sea confiar más en modelos de aprendizaje automático que sean más fáciles de interpretar. Por ejemplo, los modelos aditivos generalizados (GAM) o las máquinas de refuerzo explicables (EBM). Estas opciones son un gran sustituto a la necesidad de explicaciones. Hasta la fecha, no existen soluciones universalmente reconocidas para todos los casos posibles en el problema del desacuerdo. Pero como se trata de un problema conocido, quizá algún día podamos explicar con seguridad cualquier modelo de aprendizaje automático que creemos. 

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/13729

El papel de la calidad del generador de números aleatorios en la eficiencia de los algoritmos de optimización El papel de la calidad del generador de números aleatorios en la eficiencia de los algoritmos de optimización
En este artículo, analizaremos el generador de números aleatorios Mersenne Twister y lo compararemos con el estándar en MQL5. También determinaremos la influencia de la calidad del generador de números aleatorios en los resultados de los algoritmos de optimización.
Gestor de riesgos para el trading manual Gestor de riesgos para el trading manual
En este artículo vamos a discutir en detalle cómo escribir una clase de gestor de riesgos para el comercio manual a partir de cero. Esta clase también puede utilizarse como clase base para que la hereden los traders algorítmicos que utilizan programas automatizados.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Modelo de aprendizaje profundo GRU en Python usando ONNX en asesores expertos, GRU vs LSTM Modelo de aprendizaje profundo GRU en Python usando ONNX en asesores expertos, GRU vs LSTM
El artículo está dedicado al desarrollo de un modelo de aprendizaje profundo GRU ONNX en Python. En la parte práctica, implementaremos este modelo en un asesor comercial y, a continuación, compararemos el rendimiento del modelo GRU con LSTM (memoria a largo plazo).