English Русский Deutsch 日本語
preview
O Problema da Discordância: Mergulhando Mais Fundo na Complexidade da Explicabilidade em IA

O Problema da Discordância: Mergulhando Mais Fundo na Complexidade da Explicabilidade em IA

MetaTrader 5Aprendizado de máquina | 31 julho 2024, 09:52
70 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

O Problema da Discordância

O problema da discordância é uma área chave de pesquisa dentro da Inteligência Artificial Explicável (XAI). A XAI visa nos ajudar a entender como os modelos de IA tomam decisões, mas isso é mais fácil falar do que fazer.

Todos estamos cientes de que os modelos de aprendizado de máquina e os conjuntos de dados disponíveis estão crescendo cada vez mais em tamanho e complexidade. Na verdade, os cientistas de dados que desenvolvem algoritmos de aprendizado de máquina não podem explicar exatamente o comportamento de seu algoritmo em todos os possíveis conjuntos de dados. A Inteligência Artificial Explicável (XAI) nos ajuda a construir confiança em nossos modelos, explicar sua funcionalidade e validar que os modelos estão prontos para serem implantados em produção; mas, por mais promissor que isso possa parecer, este artigo mostrará ao leitor por que não podemos confiar cegamente em qualquer explicação que possamos obter de qualquer aplicação da tecnologia de Inteligência Artificial Explicável. 

Índice

  1. Introdução
  2. Visão Geral dos Métodos de Explicabilidade
  3. Explicações Globais e Explicações Locais
  4. Explicações Agnósticas ao Modelo e Explicações Específicas ao Modelo
  5. Definindo o Problema da Discordância e Fatores que Contribuem para o Problema
  6. Estudo de Caso
  7. Conclusão
  8. Recomendações


Introdução

O aprendizado de máquina nos permite aprender relações e interações que existem em nossos dados, mas como podemos aprender as relações e interações que existem dentro do nosso modelo? A melhor maneira de respondermos a essa pergunta é empregando técnicas de explicação de modelo. Em nossa discussão, consideraremos algumas técnicas diferentes de explicação. As técnicas de explicação de modelo nos permitem responder a perguntas da seguinte natureza:

  • Quais características nosso modelo encontrou mais informativas?
  • Se temos 1000 características em nosso conjunto de dados, como podemos separar as mais e menos informativas?
  • À medida que uma característica muda, como a saída do nosso modelo muda?
  • Quais características podem valer a pena um maior desenvolvimento?

As respostas às perguntas acima têm valor material, no entanto, há uma barreira significativa que pode bloquear nosso caminho. O desafio é que, às vezes, podemos observar discordâncias entre explicações que avaliam o mesmo modelo. Infelizmente, no momento da redação, não há procedimentos globalmente reconhecidos sobre como lidar com o problema; no entanto, hoje nos resolveremos a construir nossa própria estrutura para mitigar o problema.

Há uma tendência global buscando integrar a Inteligência Artificial em um espectro mais amplo de aplicações. No entanto, devemos ser capazes de explicar minuciosamente nossos modelos, qualquer associação que eles tenham aprendido e seu processo de tomada de decisões antes de podermos considerar qualquer pensamento de confiar no modelo. Essa propriedade desejada é chamada de "Explicabilidade". A Inteligência Artificial Explicável (XAI) tem um vasto potencial para rastrear como qualquer modelo dado está chegando às suas previsões. A Inteligência Artificial Explicável existe porque, à medida que nossas técnicas de modelagem se tornam mais sofisticadas, nossa capacidade de interpretá-las gradualmente diminui. 

Para começar, primeiro trabalharemos em um exemplo simples usando conceitos que a maioria dos leitores já pode ter conhecimento de domínio. Ao primeiro trabalharmos juntos em um problema mais simples, desenvolveremos rapidamente uma intuição para diferentes técnicas de explicabilidade. Depois de completar este exemplo, estaremos preparados para aplicar nossas habilidades em qualquer modelo de aprendizado de máquina treinado em dados de mercado reais que buscaremos em nosso Terminal MetaTrader 5.

O exemplo que consideraremos é um problema simples de estimar o salário de um atleta dadas as habilidades físicas desse atleta. É óbvio para nós que, à medida que as habilidades físicas de um atleta florescem, eles podem comandar e esperar salários mais altos, mas a questão é: quais habilidades físicas têm mais influência sobre aumentos salariais?

O conjunto de dados que usaremos em nosso problema de exemplo foi meticulosamente compilado pelos titãs dos videogames Electronic Arts como parte de sua prolífica série de videogames “Madden NFL”, permitindo que os jogadores simulem partidas jogando como seu futebolista americano profissional favorito. O conjunto de dados possui estatísticas detalhadas sobre jogadores de futebol americano profissionais. Treinaremos 4 modelos diferentes para prever o salário de um jogador com base em características sobre o jogador, como sua idade, velocidade de corrida e força. A partir daí, aplicaremos diferentes técnicas de explicação de modelo e observaremos quais insights podemos aprender sobre a relação entre os atributos do jogador e o salário do jogador. Cobriremos como cada técnica de explicação é interpretada e, em seguida, observaremos se temos alguma explicação contraditória. Rotularemos as discordâncias, faremos tentativas de decifrar o que pode estar contribuindo para as discordâncias e discutiremos possíveis soluções. 


EA "Madden NFL" Série de Videogames

Fig 1: Jogo de Videogame Madden NFL da Electronic Arts.


Visão Geral dos Métodos de Explicabilidade

De maneira geral, os métodos de explicabilidade podem ser classificados de várias maneiras. A maneira mais simples de particioná-los é em duas classes: explicadores de caixa branca e explicadores de caixa preta. No artigo anterior, discutimos modelos de caixa de vidro e modelos de caixa preta. Hoje não estamos focados nos modelos preditivos, então a "caixa preta" que nos referimos não é um modelo de aprendizado de máquina complexo e difícil de interpretar. Estamos nos referindo aos algoritmos de explicabilidade que nos ajudam a interpretar o modelo subjacente.

Uma técnica de explicação de caixa preta é projetada para ser usada em qualquer tipo de modelo; elas também são chamadas de explicadores agnósticos ao modelo.

Uma técnica de explicação de caixa branca é projetada para explorar a estrutura de um tipo específico de modelo subjacente para fornecer explicações que podem ser mais fiéis ao modelo subjacente.

Como seria de esperar, diferentes métodos de explicabilidade irão:

  • Assumir diferentes suposições sobre a forma e estrutura dos dados subjacentes.
  • Definir e avaliar diferentes métricas para entender o modelo.
  • Falhar sob diferentes condições.

Os algoritmos de explicabilidade também podem ser separados por sua metodologia. Por exemplo, existem técnicas de explicação que ganham insights ajustando as entradas de cada recurso uma de cada vez e observando as mudanças subsequentes nas previsões. Essas técnicas podem ser classificadas como técnicas baseadas em perturbação. Por outro lado, existem alguns algoritmos de explicabilidade que buscam entender o quão sensível o modelo é às mudanças nos recursos. Essas técnicas podem ser classificadas como técnicas baseadas em gradiente porque tomam a derivada da saída do modelo em relação aos seus recursos.

Hoje, cobriremos apenas alguns tipos diferentes de algoritmos explicativos. Existem muitas outras técnicas de explicação que existem além da nossa discussão e, portanto, esta lista não é de forma alguma exaustiva.

Por fim, é importante gerenciar nossas expectativas. Construir um modelo capaz de prever o preço de um valor mobiliário com uma precisão superior a 50% não garante necessariamente negociação lucrativa. 


Explicações Globais e Explicações Locais

Se quisermos saber mais sobre a importância das características e o comportamento geral do nosso modelo, precisamos empregar técnicas de explicação global. Por outro lado, se quisermos entender como nosso modelo chegou a uma previsão específica em detalhes, então precisamos de explicações locais. 

As explicações locais nos ajudam a entender a relação entre os recursos e o alvo em um nível granular e nos ajudam a construir confiança nas previsões do nosso modelo. Além disso, podemos entender melhor quais características estão contribuindo para previsões falsas e considerar a engenharia dessas características para extrair mais informações úteis delas.

Então, em nosso exemplo simples, se quisermos saber quais características são importantes para prever o salário de um atleta, precisamos aplicar técnicas de explicação global, como a importância da permutação. Além disso, se quisermos entender a previsão de um atleta em particular em mais detalhes, precisamos de técnicas de explicação local, como LIME. 


Vamos começar com nosso exemplo entendendo explicações globais do nosso modelo.

Como sempre, começamos instalando as dependências de que precisamos.

Neste caso, precisamos instalar:

  • Shap
  • eli5
  • Lime
  • Interpret
  • alibi

pip install alibi shap lime eli5 interpret

Em seguida, carregamos nossas dependências habituais.

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

Agora podemos ler o conjunto de dados de exemplo.

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

Vamos dar uma olhada em algumas linhas e colunas do conjunto de dados.

Nosso Conjunto de Dados Madden

Fig 2: Nosso conjunto de dados de exemplo 

Para simplificar, manteremos apenas algumas das características numéricas. As características categóricas requerem técnicas de codificação e detalhes que não estão alinhados com o escopo da nossa discussão.

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

Agora definiremos nosso alvo.

target = "totalSalary"

Em seguida, configuraremos os modelos que usaremos.

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

Depois de importar as dependências acima e antes de inicializar nossos modelos de aprendizado de máquina, aplicamos etapas de pré-processamento para dimensionar e padronizar os dados e realizar divisões rotineiras de treinamento/teste. Note que essas etapas são omitidas para manter o artigo fácil de ler do início ao fim. A divisão treino/teste resultou em dados de treinamento armazenados nas variáveis "x_train" e "y_train", dados de validação "x_valid" e "y_valid" e, por fim, dados de teste "x_test" e "y_test".

Vamos configurar cada um dos modelos com os quais trabalharemos hoje.

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

Em seguida, ajustaremos cada modelo.

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

Explicações Globais

Começaremos com explicações globais para nosso modelo de regressão linear. A técnica que usaremos é a importância da permutação. É uma técnica de explicação global de caixa preta.

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

Vamos obter explicações globais para o modelo de regressão linear

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

Explicações globais para nosso modelo de regressão linear

Fig 3: Explicações Globais para Nosso Modelo de Regressão Linear

Vamos interpretar os resultados juntos. A importância da permutação assume que cada uma das colunas do conjunto de dados é independente. Portanto, acredita que pode alterar uma coluna por vez para observar a mudança nas métricas de erro do modelo e usar isso para decidir quais características são importantes. Se a característica fosse importante, embaralhar seus valores causaria um aumento nas métricas de erro do modelo, e inversamente, se a característica não fosse importante, as métricas de erro do modelo diminuiriam após embaralhar os valores nessa coluna. Em casos em que a suposição de independência não é verdadeira, devemos interpretar esses resultados com cautela.

Na tabela acima, as características importantes têm coeficientes positivos e as características não importantes têm coeficientes negativos. "yearsPro", que é o tempo de experiência do atleta, tem um coeficiente negativo, mas sabemos que, na verdade, importa. Nosso modelo descobriu um fenômeno do qual não estamos cientes ou nosso explicador não está nos dando uma representação fiel da verdade? 

A importância da permutação pode embaralhar a experiência de um atleta de forma irrealista, potencialmente tornando-o jovem demais para seu nível de experiência ou com mais experiência do que sua idade total. Além disso, este método ignora quaisquer interações que possam estar presentes no conjunto de dados e credita todas as mudanças nas métricas de erro do modelo a essa característica isoladamente. Portanto, seus resultados podem ser potencialmente irreais, enfatizando a necessidade de cautela e compreensão do algoritmo em vez de aplicá-lo cegamente.No entanto, a longo prazo, pode nos fornecer informações valiosas sobre as características mais importantes do modelo. Neste caso, a habilidade do atleta de pegar a bola e sua agilidade são algumas das características mais importantes. Isso faz sentido, pois no futebol americano, espera-se que você consiga pegar passes longos de seus colegas de equipe, mesmo sob muita pressão. Além disso, após pegar a bola, você deve evitar o maior número possível de competidores sem soltá-la. Portanto, a importância dada a essas características faz sentido.


Explicações Locais

Como dissemos anteriormente, as explicações locais nos ajudam a entender como cada característica afetou cada previsão individual que nosso modelo está fazendo.

Obteremos explicações locais para as expectativas de salário de Richard LeCounte, fornecidas pela nossa Rede Neural Profunda.


Richard Le Counte

Fig 4: Richard LeCounte III

Primeiro, importamos o LIME.

LIME é um acrônimo para Explicações Interpretáveis Localmente e Agnósticas ao Modelo (Locally Interpretable Model-Agnostic Explanations). Ele explicará quais características contribuíram para a saída do modelo e se essas contribuições estavam aumentando ou diminuindo a saída. 

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)



Explicações DNN LIME

Fig 5: Explicações LIME para as Expectativas de Salário da Nossa Rede Neural Profunda para Richard LeCounte


Os gráficos acima têm muitas informações para nós. Primeiro de tudo, podemos ver quais habilidades físicas de LeCounte aumentaram sua expectativa de salário e quais a diminuíram. Nossa rede neural esperava que a classificação de dureza de LeCounte diminuísse sua expectativa de salário. Isso faz sentido porque LeCounte tem uma classificação de dureza média. Além disso, a rede neural profunda identificou corretamente as forças de LeCounte e esperava que seu salário aumentasse devido a essas forças. Isso é como esperamos que nosso modelo funcione. As explicações locais são ferramentas poderosas para validar nosso modelo em vez de confiar cegamente nele.


Explicações Agnósticas ao Modelo e Explicações Específicas ao Modelo

Em casos onde estamos cientes da estrutura subjacente do modelo, podemos usar técnicas de explicação projetadas especificamente para essa arquitetura de modelo. Essas são comumente referidas como explicadores de caixa branca (white-box explainers). Na prática, os explicadores de caixa branca podem ser mais eficientes computacionalmente do que seus equivalentes de caixa preta.

Os explicadores de caixa preta são agnósticos ao modelo, o que significa que podem interpretar e explicar qualquer modelo apresentado a eles. O algoritmo LIME que acabamos de cobrir é um exemplo de explicador de caixa preta, está no nome (Locally Interpretable Model-Agnostic Explanations). 

Usar explicadores de caixa branca quando estamos certos das propriedades subjacentes do modelo pode nos ajudar a renderizar explicações mais fiéis do comportamento do nosso modelo. Enquanto isso, os explicadores de caixa preta provavelmente introduzem alguns níveis de viés ao explicar nossos modelos. Os explicadores de caixa preta fazem algumas suposições simplificadoras sobre a forma do modelo com o qual estão lidando, se essas suposições forem violadas, o explicador de caixa preta se torna não confiável.

Como regra geral, se você está certo das propriedades subjacentes do modelo, pode ser melhor, a longo prazo, usar explicadores de caixa branca. 

Vamos dar uma olhada em um explicador de caixa preta em ação. A biblioteca SHAP tem implementações de explicadores de caixa preta SHAP. Vamos demonstrar uma abaixo. 

Importar SHAP

Importar SHAP

Calcular valores SHAP

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

Plotar nossas explicações de caixa preta

shap.plots.beeswarm(shap_values)


Explicações de Caixa Preta SHAP

Fig 6: Explicações de Caixa Preta SHAP para nosso modelo XGB


A largura de cada linha é determinada por valores atípicos e não transmite nenhuma informação sobre a importância da característica. Avaliamos a importância da característica considerando a colocação vertical de cada característica ao longo do gráfico. Quanto mais alta a característica, mais importante ela foi percebida pelo algoritmo SHAP.

Além disso, observe que em algumas características os pontos azuis e rosa estão bem separados, enquanto em outras linhas os pontos azuis e rosa estão misturados. Quando os pontos estão misturados, isso geralmente é um sinal de um efeito de interação entre essa característica e outra característica ou conjunto de características. Em resumo, os gráficos SHAP nos ajudam a resumir muitas informações em apenas um gráfico. 

Cada ponto representa o valor Shapley para cada um dos nossos pontos de dados. Isso nos permite entender rapidamente como cada ponto de dados individual afetou a previsão do modelo em um nível granular. A cor de cada ponto representa a direção e a magnitude da influência que esse ponto de dados teve na previsão do nosso modelo. Azul indicando uma diminuição na saída do modelo e rosa indicando um aumento. Lembre-se de que o algoritmo SHAP assume que as características são independentes, ou seja, ignora quaisquer interações no conjunto de dados. Violações dessa suposição podem tornar o algoritmo não confiável. 

Agora, consideraremos um explicador de caixa branca projetado para modelos baseados em árvores, como nosso modelo XGB. Usamos o método especial TreeExplainer() projetado para explicar modelos baseados em árvores.

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

Então podemos plotar as explicações de caixa branca

shap.plots.beeswarm(tree_shap_values)

Explicação de Caixa Branca SHAP

Fig 7: Explicações SHAP de caixa branca para o nosso modelo XGB

Compare os 4 principais recursos mais importantes da explicação SHAP de caixa preta e da explicação SHAP de caixa branca, você consegue ver o problema da discordância em ação? 


Definindo o Problema da Discordância e Fatores que Contribuem para o Problema

Então, agora que entendemos o que são as explicações de modelo, podemos começar a focar mais diretamente no problema da discordância. Vamos começar revisando o que é uma discordância.
Vamos dissecar o que constitui uma discordância no domínio das explicações de importância de recursos:
  • Permutações de Recursos: A primeira forma de discordância ocorre quando as permutações de recursos diferem. Se a ordem específica da importância dos recursos tem peso no seu fluxo de trabalho, a emergência de diferentes permutações entre explicações torna-se um ponto crucial de contenda. Este aspecto torna-se particularmente importante quando a sequência sutil dos recursos tem significância para a sua análise.
  • Conjuntos de Recursos Melhor Classificados: Aprofundando, a discordância se estende aos conjuntos de recursos melhor classificados. Quando a aderência estrita à permutação exata da importância dos recursos é relaxada, a atenção se volta para o conjunto dos principais recursos. Uma disparidade nos 3, 5 ou qualquer 'n' recursos principais identificados por diferentes explicadores para o mesmo modelo torna-se um ponto de divergência significativo na sua jornada analítica.
  • Flutuação do Sinal do Coeficiente: O coração de muitos explicadores está nos coeficientes atribuídos aos recursos, indicando sua contribuição positiva ou negativa para o resultado desejado. Uma mudança no sinal desse coeficiente entre duas explicações para o mesmo modelo é um sinal no seu fluxo de trabalho. Tais flutuações servem como indicadores de discordância, incentivando uma inspeção mais detalhada do funcionamento interno do modelo.
  • Ordem Relativa Entre Recursos de Interesse: Focando em recursos específicos de interesse, a discordância assume outra dimensão. Se o seu foco é comparar a importância de um recurso em relação a outro, mudanças na ordem desses dois recursos entre diferentes explicações tornam-se o campo de batalha da discordância no seu fluxo de trabalho.

O Que Não é uma Discordância?

É igualmente crucial reconhecer que nem todas as disparidades significam discordância. Vamos explorar instâncias onde as explicações mantêm uma coexistência harmoniosa:

  • Valores de Importância Divergentes: As explicações evitam uma verdadeira discordância quando os valores de importância dos recursos, conforme calculados por diferentes explicadores, não se alinham precisamente. O ponto crucial está no reconhecimento de que algoritmos diversos produzem cálculos distintos, tornando comparações diretas desafiadoras. A ênfase aqui não está na igualdade numérica dos valores de importância, mas nas percepções consistentes derivadas de explicações distintas.
  • Percepções Consistentes Sobre Valores Exatos: A essência da não-discordância está enraizada na busca de percepções consistentes em vez de uma obsessiva busca por paridade numérica exata. O objetivo não é exigir que cada explicador espelhe precisamente os valores de importância de seus equivalentes. Em vez disso, o foco muda para a narrativa mais ampla — garantindo que as explicações, embora numericamente distintas, convergem para nos guiar em direção a percepções consistentes e confiáveis do modelo.
  • Complexidade do Modelo e Desafios de Estimativa: À medida que o tecido da complexidade do modelo se desenrola, emerge uma verdade fundamental: a dificuldade de estimar com precisão o funcionamento interno do modelo cresce proporcionalmente. A não-discordância é um reconhecimento dessa complexidade inerente. Diante de modelos intrincados, a divergência nos valores de explicação torna-se um desafio esperado em vez de uma causa para alarme. Essa perspectiva se alinha com a natureza evolutiva da IA, onde a busca pela transparência encontra intricacias crescentes.

Por Que os Explicadores Discordam Entre Si?
Há muitas razões pelas quais os explicadores podem discordar entre si, vamos tentar entender alguns casos juntos.

Comparando Explicadores Globais e Locais

Uma razão pela qual podemos observar discordâncias é porque podemos estar comparando explicações globais e locais. Embora este seja um erro muito simples de cometer, gostaria de acreditar que nenhum dos leitores deste artigo cometerá esse erro. Explicações globais e locais não podem ser facilmente comparadas diretamente entre si. A menos que você tenha um entendimento detalhado de como os algoritmos são implementados sob o capô, comparar explicações globais e locais não é aconselhável e fazer isso provavelmente resultará em explicações divergentes.

Comparando Explicadores de Caixa Preta e Caixa Branca

Além disso, outra fonte de discordâncias pode ser a comparação de explicadores de caixa preta e caixa branca. Lembre-se de que os explicadores de caixa preta tentam fornecer a utilidade de explicar qualquer algoritmo, portanto, fazem certas suposições simplificadoras sobre o modelo ou o comportamento do modelo. Essas suposições que o explicador de caixa preta faz permitem que ele seja robusto e funcione bem em uma ampla gama de circunstâncias, no entanto, quando as suposições não são verdadeiras, o explicador introduz viés. Esse viés pode fazer com que as explicações de caixa preta e caixa branca difiram. 

Mudando a Ordem dos Recursos

Nossos problemas não terminam aqui, uma causa astuta para discordâncias pode ser qualquer mudança na ordem em que os recursos são apresentados. Algumas técnicas de explicação são sensíveis à ordem das entradas do modelo, enquanto outras não são. Por exemplo, a Informação Mútua não é sensível à ordem das entradas, mas o SHAP é! Portanto, é possível que um explicador SHAP possa falhar em concordar consigo mesmo após observar modelos idênticos.  

Por exemplo, abaixo apresentamos duas explicações SHAP obtidas de dois XGBRegressors idênticos. A única diferença entre os dois modelos é a ordem dos recursos que eles recebem como entradas, além disso, eles estão aprendendo com o mesmo conjunto de dados e têm o mesmo alvo. 


Valores Iniciais do SHAP

Fig 8: Nossos Valores Iniciais SHAP


SHAP Embaralhado

Fig 9: Valores SHAP Após Alterar a Ordem dos Recursos

Após embaralhar a ordem em que os recursos são apresentados ao modelo, podemos ver que nossas explicações agora discordam entre si. O recurso de altura subiu repentinamente em importância, mesmo que nenhuma nova informação tenha sido apresentada. Os 3 principais permaneceram inalterados, mas a maioria dos recursos subiu e desceu, mudando de posição. Se você planeja usar técnicas de bootstrapping ou outras formas de reamostragem ou seleção de subconjuntos, deve pelo menos estar ciente de que esse fenômeno é normal e esperado. 


Além disso, diferentes implementações da mesma técnica de explicação podem não concordar entre si devido a pequenas diferenças em seus cálculos. Por exemplo, a implementação de Permutation Importance do eli5 é sensível à ordem das entradas, mas a implementação da mesma técnica de explicação pelo sklearn não é. Isso é possível porque a mesma técnica de explicação pode ser implementada de várias maneiras. 

Para demonstrar que a implementação de Permutation Importance do eli5 é sensível à ordem das entradas, abaixo apresentamos duas explicações obtidas usando a implementação de Permutation Importance do eli5. Obtivemos os resultados de dois XGBRegressors idênticos. A única diferença entre os dois modelos é a ordem dos recursos que eles recebem como entradas, além disso, eles estão aprendendo com o mesmo conjunto de dados e têm o mesmo alvo. 


Importância Inicial da Permutação

Fig 10: Importância Inicial da Permutação


Importância da Permutação Embaralhada

Fig 11: Importância da Permutação Embaralhada


Observe que "kickPower_rating" inicialmente tinha um coeficiente positivo, mas após mudar a ordem dos recursos, agora tem um coeficiente negativo! Tais flutuações voláteis são consideradas fortes discordâncias. Os recursos mais importantes mudaram, a ordem relativa entre os recursos também mudou.

Por fim, um dos elefantes na sala é provavelmente a divergência metodológica — um fator crucial que dá origem ao fenômeno intrigante dos explicadores discordando entre si, embora estejam explicando o mesmo modelo. Como cada explicador calcula métricas diferentes sobre o modelo, eles podem interpretar o modelo de maneiras diferentes.

Felizmente para nós, o XGB tem uma função implementada que nos permite avaliar a importância dos recursos. Vamos aproveitar essa função para observar qual técnica de explicação nos dá uma representação fiel da verdade. Para ajudar o leitor, incluímos os resultados das explicações anteriores aqui, para que você não precise ficar rolando para cima e para baixo.


Verdade Base do XGB

Fig 12: Importância dos Recursos do XGB

Importância da Permutação do XGB

Fig 13: Importância da Permutação do XGB


Explicação de Caixa Branca SHAP

Fig 14: Explicações SHAP de Caixa Branca


Explicações de Caixa Preta SHAP

Fig 15: Explicações SHAP de Caixa Preta


Explainable Boosting Machine

Fig 16: Um Modelo de Caixa de Vidro Baseado em Árvore para Importância dos Recursos

A classificação de conscientização foi o recurso mais importante para o nosso modelo, no entanto, nosso pequeno conjunto de técnicas de explicação de caixa preta falhou em nos explicar isso, perdendo completamente os 3 principais recursos. Isso é um lembrete sóbrio de que as explicações são apenas estimativas da importância dos recursos, e não a verdade absoluta. Além disso, observe que "yearsPro" foi repetidamente classificado entre os principais, mas na verdade não era tão importante, isso nos alerta contra a suposição simplista de que recursos que se classificam repetidamente no topo de diferentes testes são inerentemente importantes. 

O explicador de caixa preta que aplicamos ao nosso modelo XGB contradiz o explicador de caixa branca que aplicamos, mas no final ambos os explicadores estavam errados. Portanto, em uma situação como essa, descobrir qual explicador é mais fiel teria sido uma completa perda de tempo, pois nenhum deles leva o leitor mais perto da verdade.

Eu também ajuste um Explainable Boosting Machine aos dados para ver se o modelo de caixa de vidro poderia ser usado como um proxy para avaliar a importância dos recursos, e embora ele tenha conseguido identificar corretamente o recurso mais importante do conjunto de dados, pode não ser sempre uma opção viável, especialmente se o modelo que você está tentando explicar não for baseado em árvore. Infelizmente, no entanto, o modelo de caixa de vidro também encontrou o recurso "yearsPro" como informativo, mesmo que não fosse, isso significa que também devemos estar cientes de que nossos explicadores podem concordar com informações falsas!


Estudo de Caso

Agora estamos prontos para começar a trabalhar com dados reais de mercado, começamos extraindo dados do Terminal MetaTrader 5. Nossa estratégia envolve o desenvolvimento de um script MetaQuotes Language 5 (MQL5) adaptado para extração e transformação eficiente de dados. Começamos declarando variáveis globais.
//---Our handlers for our indicators
int ma_handle;
int rsi_handle;
int cci_handle;
int ao_handle;
int bbands_handle;
int atr_handle;

Em seguida, criaremos arrays para armazenar os valores de nossos indicadores técnicos. Essas estruturas de dados assumem um papel crucial na captura, gerenciamento e aproveitamento dos resultados dos indicadores ao longo de toda a execução do 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[];

Subsequentemente, precisamos criar um nome para nosso arquivo CSV. 

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

A partir daí, precisamos criar uma variável para armazenar quantos bares gostaríamos de solicitar.

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

Avançando para configurar nossos manipuladores de indicadores técnicos. Cada vez que configuramos um indicador técnico, precisamos especificar o Símbolo que usaremos e o intervalo de tempo que pretendemos usar. A partir daí, dependendo do indicador, você pode precisar especificar o período do indicador, o parâmetro de deslocamento, o método de suavização e qual preço deve ser aplicado ao indicador. 

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

Continuando de onde paramos, o próximo passo é transferir valores de nossos indicadores para as estruturas de dados designadas.

 //---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 começarmos o processo de escrita de arquivo, uma tarefa preliminar crucial envolve a incorporação de um manipulador de arquivo em nosso script. Esta etapa fundamental abrange a configuração de parâmetros e mecanismos necessários para permitir a criação e manipulação suaves do arquivo de saída.

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

Seguindo isso, uma fase crítica em nosso script se desenrola à medida que navegamos sistematicamente pelos arrays de preços históricos e valores de indicadores para que possamos codificar os dados no arquivo CSV designado.

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


Dados de mercado

Fig 17: Dados de mercado do terminal MetaTrader 5


Agora estamos prontos para manipular os dados e aplicar nossas novas habilidades que aprendemos com o conjunto de dados de exemplo. 

Agora importamos nossas dependências.

#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

Carregando dependências 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

Leia no csv.

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

Vamos configurar o alvo.

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

Exclua todas as linhas com valores ausentes.

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

Vamos criar uma lista de preditores.

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

Vamos dimensionar os dados.

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


Dados de mercado em escala

Fig 18: Dados de mercado em escala

Configurando nossos algoritmos de caixa preta

#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

Divisão de Treino/Teste

train = 0
train_end = 5000
test = train_end + 40

Configurando nosso modelo linear

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

Configurando nosso modelo XGB

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

Configurando nosso modelo de rede neural 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]
)


Obtendo Explicações Globais Para Nossa Rede Neural Profunda

Vamos usar o pacote alibi para obter explicações para nossa rede neural profunda. O pacote alibi possui uma implementação útil de uma técnica explicativa conhecida como Efeitos Locais Acumulados (ALE). ALE expande todas as técnicas explicativas que consideramos até agora, pois é robusto e pode lidar com características fortemente correlacionadas, relaxa as suposições de independência das características, pode ser interpretado visualmente, é computacionalmente eficiente e, ao contrário de algumas outras técnicas de explicação, o ALE não depende de suposições de linearidade, tornando-o bem adequado para capturar relações complexas nos dados.   

Primeiro calculamos nossos valores ALE.

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

Então convertemos nossos dados de entrada para o formato NumPy antes de obtermos nossos 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)

Os gráficos ALE ajudam a interpretar como as mudanças em cada característica afetam a saída do modelo. Por exemplo, abaixo temos o gráfico ALE para nosso Oscilador Magnífico. Como podemos observar, o Oscilador Magnífico pode ajudar nossa rede neural profunda a antecipar quando o preço cairá; observamos a inclinação descendente no gráfico ALE à medida que o valor do Oscilador Magnífico aumenta.

ALE AO

Fig 19: Gráfico ALE Para Nosso Oscilador Magnífico


Além disso, características que não são informativas terão um gráfico ALE que se assemelha a uma linha horizontal, o que significa que mudanças nessa característica têm pouco impacto no alvo. No nosso estudo de caso, o Índice de Força Relativa não era informativo. 

ALE RSI

Fig 20 : ALE RSI

Obtendo Explicações Globais A Partir do SHAP Agnóstico ao 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 Beeswarm Black-box SHAP do nosso modelo XGB

Fig 21: Gráfico Beeswarm Black-box SHAP do nosso modelo XGB

Nosso gráfico beeswarm nos informa que o Oscilador Magnífico é a característica mais importante neste conjunto de dados. Lembre-se de que a largura de cada linha no gráfico não transmite nenhuma informação, pois pode ser determinada simplesmente por outliers. Além disso, notamos que os pontos azuis e rosa não parecem estar embaralhados, o que significa que o conjunto de dados pode não ter termos de interação fortes.

Importância das Características Segundo Informação Mútua

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

A informação mútua discordou completamente do nosso explicador SHAP no que diz respeito às 4 principais características. Isso só lança mais dúvidas em nossa jornada já confusa. Para piorar, nosso explicador SHAP estava, na verdade, correto sobre o Oscilador Magnífico ser a característica mais importante neste caso. Portanto, confiar em vários explicadores é, na verdade, uma faca de dois gumes. Por um lado, protege você contra o viés herdado de qualquer explicação usada, mas por outro lado, cria uma probabilidade maior de surgirem desacordos. É difícil dizer qual caso é empiricamente melhor, pois tudo depende do conjunto de dados particular, do modelo particular e de muitas outras variáveis.

Por fim, consideraremos a Análise de Sensibilidade de Morris como nosso último Explicador Global Black-Box

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


Análise de Sensibilidade de Morris

Fig 22: Análise de Sensibilidade de Morris

Aqui estão as métricas de importância reais do nosso modelo XGB.

Dados de Mercado Base XGB.

Fig 23: Importância dos Dados de Mercado XGB

A média móvel foi a característica mais importante em nosso conjunto de dados, seguida pelo Oscilador Magnífico. Portanto, neste caso específico, a Análise de Sensibilidade de Morris nos forneceu as melhores explicações relativamente falando. No entanto, na maioria dos casos, você pode não ter acesso à verdade fundamental; portanto, como você teria selecionado qual explicação dar mais peso? Quão confiante você estaria em sua decisão? Como você poderia validar qualquer uma de suas decisões sobre a importância das características se não tivesse acesso às tabelas verdadeiras de importância das características? 


Conclusão

Como podemos ver, não há respostas simples aqui, há muitas partes móveis. A forma e estrutura de seus conjuntos de dados devem ser levadas em consideração, o modelo subjacente também deve ser levado em consideração, a presença de termos de interação em seus dados deve ser considerada e, acima de tudo, o funcionamento interno de cada explicação deve estar à disposição do praticante de machine learning. Além disso, em nosso exemplo controlado, observamos que é possível que todos os seus explicadores estejam errados, portanto, em tal situação, tentar resolver qualquer desacordo teria sido um desperdício fútil de tempo. Portanto, o retorno que obtemos das técnicas explicativas nem sempre justifica a complexidade que elas podem introduzir. No entanto, com o tempo, podemos perceber melhores técnicas explicativas e observar melhores algoritmos.

Recomendações

Portanto, após considerar tudo isso, estou convencido de que possivelmente a melhor solução para o problema de desacordo pode ser confiar mais em modelos de machine learning que sejam interpretáveis, como Modelos Aditivos Generalizados (GAM) ou Máquinas de Impulso Explicáveis (EBM). Esses modelos substituem perfeitamente a necessidade de explicações. Até hoje, não há soluções globalmente reconhecidas para todos os casos possíveis do problema de desacordo, mas à medida que a conscientização sobre o problema continua a crescer, talvez um dia possamos explicar com confiança qualquer modelo de machine learning que construímos. 

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/13729

Do básico ao intermediário: Variáveis (III) Do básico ao intermediário: Variáveis (III)
Aqui iremos ver como usar variáveis e constantes predefinidas pela linguagem MQL5. Além disto iremos dar uma rápida pincelada em um outro tipo especial de variável, que são as funções. Existem diversas situações em que saber como trabalhar da forma correta com tais variáveis, pode ser a diferença entre uma aplicação que funciona e uma que não funciona. O requisito para entender o que será visto aqui, é ter compreendido o que foi visto nos artigos anteriores.
Desenvolvendo um cliente MQTT para Metatrader 5: uma abordagem TDD — Parte 6 Desenvolvendo um cliente MQTT para Metatrader 5: uma abordagem TDD — Parte 6
Este artigo é a sexta parte de uma série que descreve nossas etapas de desenvolvimento de um cliente MQL5 nativo para o protocolo MQTT 5.0. Nesta parte, comentamos as principais mudanças em nosso primeiro refatoramento, como chegamos a um modelo viável para nossas classes de construção de pacotes, como estamos construindo pacotes PUBLISH e PUBACK, e a semântica por trás dos Códigos de Motivo PUBACK.
Desenvolvendo um sistema de Replay (Parte 59): Um novo futuro Desenvolvendo um sistema de Replay (Parte 59): Um novo futuro
O correto entendimento das coisas, nos permite fazer mais e com menos esforço. Neste artigo irei explicar por que temos que temporizar a colocação do template, antes do serviço realmente começar a mexer no gráfico. Além disto, que tal melhorar o indicador de mouse, para podermos fazer mais coisas com ele.
Trabalho com modelos ONNX nos formatos float16 e float8 Trabalho com modelos ONNX nos formatos float16 e float8
Os formatos de dados utilizados para representar modelos de aprendizado de máquina desempenham um papel fundamental em sua eficiência. Nos últimos anos, surgiram vários novos tipos de dados desenvolvidos especificamente para trabalhar com modelos de aprendizado profundo. Neste artigo, vamos focar em dois novos formatos de dados que se tornaram amplamente utilizados nos modelos modernos.