top of page

Inteligência Artificial: FA(irness)CES

Em 2024 a equipe do Tidymodels anunciou a introdução de ferramentas para avaliar a imparcialidade ou viés em modelos que são estimados através do universo de pacote tidymodels e o interesse desse post é, em primeiro momento, tentar reproduzir os exemplos e, após o bom entendimento desses pacotes e suas aplicações, aplicar em banco de dados públicos e que já possuem publicações relacionadas à suas avaliações de modelagem para fins de comparação em relação a essa imparcialidade comentada.


Vamos juntes?

Eu gosto M-U-I-T-O de trazer essa ideia de "faces" da Inteligência Artificial devido ao grande número de denúncias e descasos que há no Brasil (e fora do BR também) quando modelagens de IA são utilizadas sem a avaliação de imparcialidade, trazendo consigo muitas F-A-C-E-S, incluindo a face da discriminação racial (na maioria das vezes), geográfica/territorial, renda e educação.


Mas, nesse mundão da Tecnologia da Informação e de cultura ocidental, a palavra mais utilizada para retratar essa imparcialidade em modelagens é a F-A-I-R-N-E-S-S. E, como pode ser visto na imagem ao lado, a tradução de fairness é voltada a justiça e a equidade, mas sempre me questiono: De qual justiça estamos falando?


A justiça no âmbito da tecnologia da informação significa que você deve tratar dados pessoais de uma maneira que as pessoas razoavelmente esperam, ou seja, não usá-los para que haja algum efeito adverso injustificados sobre elas. Qualquer processamento de dados pessoais usando IA que leve à discriminação injusta entre pessoas violará o princípio da justiça.


E será que podemos chamar isso de viés algorítmico?

O viés é um aspecto da tomada de decisão. Essa característica é frequentemente detectada não apenas em sistemas de Inteligência Artificial, mas também em humanos e instituições, o que as vezes não é possível correr, já que "se ficar o bicho pega, se correr o bicho come" no capitalismo selvagem. Todavia, o que essa tomada de decisão vai levar esse sistema de IA aplicar inúmeras vezes e em diversos cenários, a discriminação pode surgir como um dos efeitos adversos resultantes do viés. Por exemplo, uma abordagem preconceituosa em favor de uma solução em detrimento de outra.


Conseguiu entender?

É meio confuso, mas veja que sempre é uma questão humana.


Uma vez que temos esse entendimento, vamos conhecer os pacote do universo tidymodels. Os pacotes principais do tidymodels trabalham juntos para permitir uma ampla variedade de abordagens de modelagem, são eles:


  • rsample que fornece infraestrutura para divisão e reamostragem eficiente de dados;

  • parnsip é uma interface organizada e unificada para modelos que pode ser usada para testar uma variedade de modelos sem se prender às minúcias sintáticas dos pacotes subjacentes;

  • recipes é uma interface organizada para ferramentas de pré-processamento de dados para engenharia de recursos;

  • workflows (fluxos de trabalho) agrupam pré-processamento, modelagem e pós-processamento;

  • tune ajuda a otimizar os hiperparâmetros do seu modelo e as etapas de pré-processamento;

  • yardstick (critério) mede a eficácia dos modelos usando métricas de desempenho;

  • broom converte as informações em objetos R estatísticos comuns em formatos previsíveis e fáceis de usar;

  • dials cria e gerencia parâmetros de ajuste e grades de parâmetros.


A estrutura tidymodels também inclui muitos outros pacotes projetados para análise de dados especializada e tarefas de modelagem. Eles não são carregados automaticamente com library(tidymodels), então você precisará carregar cada um com sua própria chamada para library(), mas aqui o principal pacote a ser comentado é o


yardstick

A versão 1.3.0 do pacote yardstick introduziu uma implementação para métricas de grupo. O caso de uso que motiva a implementação desta funcionalidade são as métricas de justiça, embora as métricas de grupo tenham aplicações além desse domínio. As métricas de justiça quantificam o grau de disparidade em um valor de métrica entre grupos, ou seja, assim como há a métrica de acurácia, verdadeiro positivo/negativo, teremos a métrica de grupo.


A título de exemplo vamos utilizar o conjunto de dados hpc_cv, contendo probabilidades de classe e previsões de classe para uma análise discriminante linear ajustada ao conjunto de dados HPC de Kuhn e Johnson (2013).


library(yardstick)
library(dplyr)

Esses dados são os conjuntos de avaliação de um esquema de validação cruzada de 10 folds.


A validação cruzada K-Fold é uma técnica em que o conjunto de dados é dividido em "k" subconjuntos (dobras) para avaliar o desempenho do modelo de forma mais fiável.

Este quadro de dados possui as variáveis de classe verdadeira (obs), a previsão da classe (pred) e as colunas para cada probabilidade de classe (colunas VF, F, M e L). Além disso, é incluída uma coluna para o indicador de reamostragem (resample), indicando o fold em que a observação foi inserida.


dados_exemplo1 <- tibble::tibble(yardstick::hpc_cv)
head(dados_exemplo1, 5)

Para os propósitos do exemplo, também adicionaremos uma coluna denominada por batch processing aos dados e selecionaremos as colunas para as probabilidades de classe, das quais não precisamos. O comando set.seed utilizaremos para que os resultados inseridos neste post seja reproduzivel.


set.seed(1)

Digite em sua linha de comando:


hpc <- dados_exemplo1 %>% 
       dplyr::mutate(batch = base::sample(c("a", "b"), nrow(.), replace = TRUE)) %>% 
       dplyr::select(-c(VF, F, M, L))

O processamento em lote oferece uma série de benefícios para eficiência, redução de custos, melhoria da precisão apoiando a otimização da precisão dos processos de negócios, reduzindo a probabilidade de erros humanos e auxílio na tomada de decisão.

Mesmo antes da implementação das métricas de grupo, todas as métricas de referência já tinham consciência do grupo. Quando dados agrupados são passados para uma métrica com reconhecimento de grupo, eles retornarão valores de métrica calculados para cada grupo. Logo, se temos 10 folds, e se quiséssemos calcular a precisão do modelo reamostrado, poderíamos escrever:


hpc %>% 
  dplyr::group_by(Resample) %>%
  yardstick::accuracy(obs, pred)

O resultado pode ser apreciado abaixo.

# A tibble: 10 × 4
   Resample .metric  .estimator .estimate
   <chr>    <chr>    <chr>          <dbl>
 1 Fold01   accuracy multiclass     0.726
 2 Fold02   accuracy multiclass     0.712
 3 Fold03   accuracy multiclass     0.758
 4 Fold04   accuracy multiclass     0.712
 5 Fold05   accuracy multiclass     0.712
 6 Fold06   accuracy multiclass     0.697
 7 Fold07   accuracy multiclass     0.675
 8 Fold08   accuracy multiclass     0.721
 9 Fold09   accuracy multiclass     0.673
10 Fold10   accuracy multiclass     0.699

Veja que aqui nós temos como produto a estimação da acurácia de cada fold ou grupo de interesse.


Esse comportamento é o que entendemos por consciência de grupo.

As métricas de grupo são associadas a uma coluna de dados de modo que, quando os dados são transmitidos a essa coluna, a métrica agrupará temporariamente por essa coluna, calculará valores para cada um dos grupos definidos pela coluna e, em seguida, agregará os valores calculados para o agrupamento temporário de volta ao nível de agrupamento dos dados de entrada.

Suponha que os batch nos dados representem dois grupos para os quais o desempenho do modelo não deva diferir. Para quantificar o grau em que o desempenho do modelo difere para esses dois grupos, poderíamos calcular os valores de precisão para cada grupo separadamente e, em seguida, calcular a diferença. Veja o exemplo abaixo.


acc_by_group <- hpc %>% 
                dplyr::filter(Resample == "Fold01") %>%
                dplyr::group_by(batch) %>%
                yardstick::accuracy(obs, pred)

Como resultado:

# A tibble: 2 × 4
  batch .metric  .estimator .estimate
  <chr> <chr>    <chr>          <dbl>
1 a     accuracy multiclass     0.693
2 b     accuracy multiclass     0.757

Vamos observar a diferença entre os batch:


base::diff(c(acc_by_group$.estimate[2], acc_by_group$.estimate[1]))
[1] -0.06413499

As métricas de grupo codificam a função group_by(), etapa de agregação amostrada acima em uma métrica de critério. Podemos definir uma nova métrica groupwise com a função new_groupwise_metric() do pacote new_groupwise_metric.


Vamos entender os parâmetros dessa função?

Uma função métrica de critério ou conjunto de métricas. As métricas disponíveis no pacote são diversas, mas algumas delas são: detection_prevalence, accuracy, average_precision, classification_cost, poisson_log_loss, precision, roc_auc, recall, rmse, sens e spec, e você consegue ter mais informações sobre as métricas no CRAN do pacote.


No exemplo abaixo estamos utilizando a métrica accuracy:


accuracy_diff <- yardstick::new_groupwise_metric(
                               fn = accuracy,
                               name = "accuracy_diff",
                               aggregate = function(acc_by_group){                               base::diff(c(acc_by_group$.estimate[2], acc_by_group$.estimate[1]))                               })

Veja que a saída accuracy_diff é um objeto da classe metric_factory.


class(accuracy_diff)

A partir de agora a função accuracy_diff sabe como obter valores de acurácia para cada grupo e depois retornar a diferença entre a acurácia do primeira e do segundo resultado como saída.


Combinados?

A última coisa que precisamos associar ao objeto é o nome da variável de agrupamento para a qual passar group_by(). Podemos passar o nome da variável para accuracy_diff fazer isso:


accuracy_diff_by_batch <- accuracy_diff(batch)

Logo, podemos usar a accuracy_diff_by_batch() como métrica da mesma forma que usaríamos accuracy(). Veja o exemplo abaixo:


hpc %>% 
  dplyr::filter(Resample == "Fold01") %>%
  accuracy_diff_by_batch(obs, pred)

Também podemos adicionar accuracy_diff_by_batch() aos conjuntos de métricas:


acc_ms <- yardstick::metric_set(accuracy, accuracy_diff_by_batch)
A metric set, consisting of:
- `accuracy()`, a class metric               | direction: maximize
- `accuracy_diff_by_batch()`, a class metric | direction: minimize, group-wise on: batch

Aplicando as métricas no Fold01 da base de dados:


hpc %>% 
  dplyr::filter(Resample == "Fold01") %>%
  acc_ms(truth = obs, estimate = pred)

Como resultado:

# A tibble: 2 × 4
  .metric       .estimator .estimate .by  
  <chr>         <chr>          <dbl> <chr>
1 accuracy      multiclass    0.726  NA   
2 accuracy_diff multiclass   -0.0641 batch

Veja que a saída .by nos informa que o agrupamento foi feito através do batch, o que nos traz como inferência que as métricas de grupo reconhecem o grupo. Quando essa operação é aplicada a dados com quaisquer variáveis com agrupamentos diferentes da coluna passada como o primeiro argumento para accuracy_diff(), neste caso, accuracy_diff_by_batch(), as métricas se comportarão como qualquer outra métrica de critério. Por exemplo:


hpc %>% 
  dplyr::group_by(Resample) %>%
  accuracy_diff_by_batch(obs, pred)
# A tibble: 10 × 5
   Resample .metric       .by   .estimator .estimate
   <chr>    <chr>         <chr> <chr>          <dbl>
 1 Fold01   accuracy_diff batch multiclass  -0.0641 
 2 Fold02   accuracy_diff batch multiclass  -0.0460 
 3 Fold03   accuracy_diff batch multiclass  -0.0702 
 4 Fold04   accuracy_diff batch multiclass  -0.0432 
 5 Fold05   accuracy_diff batch multiclass   0.0460 
 6 Fold06   accuracy_diff batch multiclass  -0.0201 
 7 Fold07   accuracy_diff batch multiclass  -0.0213 
 8 Fold08   accuracy_diff batch multiclass  -0.0692 
 9 Fold09   accuracy_diff batch multiclass   0.0971 
10 Fold10   accuracy_diff batch multiclass  -0.00461

As métricas de grupo formam o back-end das métricas de fairness em modelos organizados e, a partir dos conhecimentos adquiridos até o momento, vamos iniciar com a métrica demographic_parity.


A função demographic_parity tem o objetivo que avaliar a paridade demográfica e essa é satisfeita quando as previsões de um modelo têm a mesma taxa positiva prevista entre os grupos. Seu único parâmetro é o by(). Esse parâmetro é o identificador de coluna do recurso confidencial. Este deve ser um nome de coluna sem aspas, referindo-se a uma coluna nos dados não pré-processados.


Vamos entender um pouco mais?

Esta função gera uma função métrica de critério de justiça. Dada uma variável de agrupamento by, a função demographic_parity() retornará uma função métrica de critério, como vimos anteriormente, que está associada ao agrupamento de variáveis de dados do parâmetro by e a um pós-processador. A função gerada primeiro gerará um conjunto de valores de métrica de detection_prevalence por grupo antes de resumir entre os grupos usando a função de pós-processamento.


A função gerada possui apenas um método de quadro de dados e deve ser usada como parte de um conjunto de métricas.

Por padrão, essa função considera a diferença no intervalo da métrica detection_prevalence a partir do .estimate entre os grupos. O que significa que a disparidade entre pares entre os grupos é o valor de retorno da função. Vamos atualizar o grupo de métricas e compilar o exemplo abaixo. Veja que a indicação de grupos será a variável Resample.


acc_ms <- yardstick::metric_set(sens, accuracy, accuracy_diff_by_batch, demographic_parity(Resample))

Aplicando a base de dados:


hpc %>%
  acc_ms(truth = obs, estimate = pred)

O que o resultado .estimate está nos contando?

Um valor 0 (ou próximo de 0) indica paridade entre os grupos. Observe que esta definição não depende do verdadeiro resultado. O parâmetro truth é incluído nas métricas geradas para fins de consistência. Esse resultado é esperado, visto que todos os folds estão balanceados de acordo com o número de observações.


table(hpc$Resample)
Fold01 Fold02 Fold03 Fold04 Fold05 Fold06 Fold07 Fold08 Fold09 Fold10 
   347    347    347    347    347    347    345    348    346    346 

A função equal_opportunity() é satisfeita quando as previsões de um modelo têm as mesmas taxas de verdadeiros positivos e falsos negativos em todos os grupos protegidos. O cálculo dessa função é baseado na diferença entre o maior e o menor valor da função sens() entre grupos, em que sens() calcula a sensibilidade de uma mensuração para um resultado de referência (o parâmetro "truth" ou algum padrão ouro).


O parâmetro da função é idêntico ao da função demographic_parity().


Vamos ao exemplo?

Assim como no exemplo anterior, precisamos acrescentar a métrica no vetor de métricas que desejamos calcular.


acc_ms <- yardstick::metric_set(sens, accuracy, accuracy_diff_by_batch,                                demographic_parity(Resample), equal_opportunity(Resample))

Um valor 0 indica paridade entre os grupos e o nosso resultado indica que há igualdade de oportunidades por estar tão próximo quanto de 0.


hpc %>%
  acc_ms(truth = obs, estimate = pred)

A equalized_odds() é a função em que as probabilidades equalizadas são satisfeitas quando as previsões de um modelo têm as mesmas taxas de falsos positivos, verdadeiros positivos, falsos negativos e verdadeiros negativos em todos os grupos protegidos. Assim como no exemplo anterior, precisamos acrescentar a métrica no vetor de métricas que desejamos calcular.


acc_ms <- yardstick::metric_set(sens, accuracy, accuracy_diff_by_batch,                                        demographic_parity(Resample),
equal_opportunity(Resample),
equalized_odds(Resample))

Um valor 0 indica paridade entre os grupos e o nosso resultado indica que há probabilidades equalizadas.


E como está o nosso?

Todas estão próximo de 0.

hpc %>%
  acc_ms(truth = obs, estimate = pred)
# A tibble: 6 × 4
  .metric            .estimator .estimate .by     
  <chr>              <chr>          <dbl> <chr>   
1 sens               macro       5.60e- 1 NA      
2 accuracy           multiclass  7.09e- 1 NA      
3 accuracy_diff      multiclass -1.95e- 2 batch   
4 demographic_parity macro       2.78e-17 Resample
5 equal_opportunity  macro       1.03e- 1 Resample
6 equalized_odds     macro       1.03e- 1 Resample

Os exemplos aqui feitos foram somente para apresentar e simular uma avaliação de cada uma das métricas de justiça feita por esse time espetacular e, claro, trazer o contexto de avaliação de grupo, para que todos possam entender e interpretar seus resultados.


No próximo post dessa série "Inteligência Artificial: FA(irness)CES" teremos um exemplo com dados reais e utilizaremos as funções de "justiça" para avaliar sua estimação.


Espero que tenha gostado e que, a partir de agora, passe a utilizar pelo menos uma dessas funções, nem que seja só para observar o balanceamento.


Até mais.


Fernanda Kelly R. Silva | Estatística

Comments


Em um mundo onde tudo pode ser monitorado e medido, os dados são apenas a matéria-prima do conhecimento.

© 2020 - 2025 por

Fernanda Kelly R. Silva.
 

Em um mundo onde tudo pode ser monitorado e medido, os dados são apenas a matéria-prima do conhecimento. 

Follow

  • git
  • Linkedin

Follow

Follow

bottom of page