Posit AI Weblog: Lime V0.4: The Kitten Image Version


Introdução

Fico feliz em relatar um novo grande lançamento de lime pousou em Cran. lime é uma porta R da biblioteca Python com o mesmo nome de Marco Ribeiro que permite ao usuário retirar os modelos de aprendizado de máquina de máquina preta aberta e explicar seus resultados em base por observação. Ele funciona modelando o resultado da caixa preta no bairro native em torno da observação para explicar e usar esse modelo native para explicar por que (não como) a caixa preta fez o que fez. Para mais informações sobre a teoria de lime Vou direcioná -lo para o artigo
Apresentando a metodologia.

Novos recursos

A carne deste lançamento centra -se em torno de dois novos recursos que estão um pouco ligados: suporte nativo para modelos Keras e suporte para explicar modelos de imagem.

keras e imagens

JJ Allaire teve a gentileza de nomear lime Durante sua palestra, introdução do tensorflow e keras Pacotes e eu me senti compelido a apoiá -los nativamente. Como Keras é de longe a maneira mais standard de interagir com o TensorFlow, é o primeiro da linha para o suporte a construção. A adição de Keras significa que
lime Agora suporta modelos diretamente dos pacotes a seguir:

Se você está trabalhando em algo muito obscuro ou de ponta para não poder usar esses pacotes, ainda é possível tornar seu modelo lime compatível por fornecer predict_model() e model_type() Métodos para isso.

Os modelos Keras são usados como qualquer outro modelo, passando -o para o lime()
Função junto com os dados de treinamento para criar um objeto explicador. Como emblem vamos falar sobre modelos de imagens, usaremos um dos modelos Imagenet pré-treinados que estão disponíveis no próprio Keras:

Mannequin
______________________________________________________________________________________________
Layer (sort)                              Output Form                         Param #        
==============================================================================================
input_1 (InputLayer)                      (None, 224, 224, 3)                  0              
______________________________________________________________________________________________
block1_conv1 (Conv2D)                     (None, 224, 224, 64)                 1792           
______________________________________________________________________________________________
block1_conv2 (Conv2D)                     (None, 224, 224, 64)                 36928          
______________________________________________________________________________________________
block1_pool (MaxPooling2D)                (None, 112, 112, 64)                 0              
______________________________________________________________________________________________
block2_conv1 (Conv2D)                     (None, 112, 112, 128)                73856          
______________________________________________________________________________________________
block2_conv2 (Conv2D)                     (None, 112, 112, 128)                147584         
______________________________________________________________________________________________
block2_pool (MaxPooling2D)                (None, 56, 56, 128)                  0              
______________________________________________________________________________________________
block3_conv1 (Conv2D)                     (None, 56, 56, 256)                  295168         
______________________________________________________________________________________________
block3_conv2 (Conv2D)                     (None, 56, 56, 256)                  590080         
______________________________________________________________________________________________
block3_conv3 (Conv2D)                     (None, 56, 56, 256)                  590080         
______________________________________________________________________________________________
block3_pool (MaxPooling2D)                (None, 28, 28, 256)                  0              
______________________________________________________________________________________________
block4_conv1 (Conv2D)                     (None, 28, 28, 512)                  1180160        
______________________________________________________________________________________________
block4_conv2 (Conv2D)                     (None, 28, 28, 512)                  2359808        
______________________________________________________________________________________________
block4_conv3 (Conv2D)                     (None, 28, 28, 512)                  2359808        
______________________________________________________________________________________________
block4_pool (MaxPooling2D)                (None, 14, 14, 512)                  0              
______________________________________________________________________________________________
block5_conv1 (Conv2D)                     (None, 14, 14, 512)                  2359808        
______________________________________________________________________________________________
block5_conv2 (Conv2D)                     (None, 14, 14, 512)                  2359808        
______________________________________________________________________________________________
block5_conv3 (Conv2D)                     (None, 14, 14, 512)                  2359808        
______________________________________________________________________________________________
block5_pool (MaxPooling2D)                (None, 7, 7, 512)                    0              
______________________________________________________________________________________________
flatten (Flatten)                         (None, 25088)                        0              
______________________________________________________________________________________________
fc1 (Dense)                               (None, 4096)                         102764544      
______________________________________________________________________________________________
fc2 (Dense)                               (None, 4096)                         16781312       
______________________________________________________________________________________________
predictions (Dense)                       (None, 1000)                         4097000        
==============================================================================================
Whole params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
______________________________________________________________________________________________

O modelo VGG16 é um modelo de classificação de imagem que foi construído como parte da competição ImageNet, onde o objetivo é classificar as imagens em 1000 categorias com a maior precisão. Como podemos ver, é bastante complicado.

Para criar um explicador, precisaremos passar os dados de treinamento também. Para dados de imagem, os dados de treinamento são realmente usados apenas para dizer ao limão que estamos lidando com um modelo de imagem, portanto, qualquer imagem será suficiente. O formato para os dados de treinamento é simplesmente o caminho para as imagens e, como a Web é executada em fotos de gatinhos, usaremos uma delas:

img <- image_read('https://www.data-imaginist.com/belongings/photos/kitten.jpg')
img_path <- file.path(tempdir(), 'kitten.jpg')
image_write(img, img_path)
plot(as.raster(img))

Posit AI Weblog: Lime V0.4: The Kitten Image Version

Como nos modelos de texto, o explicador precisará saber como preparar os dados de entrada para o modelo. Para modelos de Keras, isso significa formatar os dados da imagem como tensores. Felizmente, Keras vem com muitas ferramentas para reformular os dados da imagem:

image_prep <- perform(x) {
  arrays <- lapply(x, perform(path) {
    img <- image_load(path, target_size = c(224,224))
    x <- image_to_array(img)
    x <- array_reshape(x, c(1, dim(x)))
    x <- imagenet_preprocess_input(x)
  })
  do.name(abind::abind, c(arrays, listing(alongside = 1)))
}
explainer <- lime(img_path, mannequin, image_prep)

Agora temos um modelo explicador para entender como a rede neural VGG16 faz suas previsões. Antes de irmos, vamos ver o que o modelo pensa de nosso gatinho:

res <- predict(mannequin, image_prep(img_path))
imagenet_decode_predictions(res)
((1))
  class_name class_description      rating
1  n02124075      Egyptian_cat 0.48913878
2  n02123045             tabby 0.15177219
3  n02123159         tiger_cat 0.10270492
4  n02127052              lynx 0.02638111
5  n03793489             mouse 0.00852214

Então, é muito certo sobre toda a coisa do gato. A razão pela qual precisamos usar
imagenet_decode_predictions() é que a saída de um modelo Keras é sempre apenas um tensor sem nome:

(1)    1 1000
NULL

Estamos acostumados a classificadores conhecendo os rótulos da classe, mas esse não é o caso das Keras. Motivado por isso, lime agora tem uma maneira de definir/substituir os rótulos da classe de um modelo, usando o as_classifier() função. Vamos refazer o nosso explicador:

model_labels <- readRDS(system.file('extdata', 'imagenet_labels.rds', package deal = 'lime'))
explainer <- lime(img_path, as_classifier(mannequin, model_labels), image_prep)

Há também um as_regressor() função que diz limesem dúvida, o modelo é um modelo de regressão. A maioria dos modelos pode ser introspectada para ver qual tipo de modelo eles são, mas as redes neurais realmente não se importam. lime
adivinha o tipo de modelo da ativação usada na última camada (ativação linear == regressão), mas se essa heurística falhar, então
as_regressor()/as_classifier() pode ser usado.

Agora estamos prontos para zombar do modelo e descobrir o que faz pensar que nossa imagem é de um gato egípcio. Mas … primeiro vou ter que falar sobre mais um conceito: superpixels (prometo que vou chegar à parte da explicação daqui a pouco).

Para criar permutações significativas de nossa imagem (lembre -se, essa é a idéia central em lime), temos que definir como fazê -lo. As permutações precisam ser substanciais o suficiente para ter um impacto na imagem, mas não tanto que o modelo não reconheça completamente o conteúdo em todos os casos – eles devem levar a um resultado interpretável. O conceito de superpixels se presta bem a essas restrições. Em suma, um superpixel é um patch de uma área com alta homogeneidade, e a segmentação de superpixel é um agrupamento de pixels de imagem em vários superpixels. Ao segmentar a imagem para explicar em superpixels, podemos ativar e desativar a área de similaridade contextual durante as permutações e descobrir se essa área é importante. Ainda é necessário experimentar um pouco, pois o número superb de superpixels depende do conteúdo da imagem. Lembre -se, precisamos que eles sejam grandes o suficiente para ter um impacto, mas não tão grande que a probabilidade de classe se torne efetivamente binária. lime Vem com uma função para avaliar a segmentação de superpixel antes de iniciar a explicação e é recomendável brincar um pouco – com o tempo que você provavelmente terá uma ideia dos valores certos:

# default
plot_superpixels(img_path)

# Altering some settings
plot_superpixels(img_path, n_superpixels = 200, weight = 40)

O padrão é definido como um número bastante baixo de superpixels – se o sujeito de interesse for relativamente pequeno, pode ser necessário aumentar o número de superpixels para que o assunto completo não acabe em um ou alguns superpixels. O weight O parâmetro permitirá que você torne os segmentos mais compactos, ponderando a distância espacial maior que a distância da cor. Para este exemplo, seguiremos os padrões.

Esteja ciente de que a explicação de modelos de imagem é muito mais pesada que os dados tabulares ou de texto. De fato, criará 1000 novas imagens por explicação (tamanho de permutação padrão para imagens) e as executará através do modelo. Como os modelos de classificação de imagens geralmente são bastante pesados, isso resultará no tempo de computação medido em minutos. A permutação é em lotes (padrão para 10 permutações por lote), portanto, você não deve ter medo de ficar sem RAM ou espaço de tração rígida.

clarification <- clarify(img_path, explainer, n_labels = 2, n_features = 20)

A saída de uma explicação de imagem é um quadro de dados do mesmo formato que a dos dados tabulares e de texto. Cada recurso será um superpixel e a linha Pixel do superpixel será usada como sua descrição. Geralmente a explicação só faz sentido no contexto da própria imagem, então a nova versão de
lime também vem com um plot_image_explanation() função para fazer exatamente isso. Vamos ver o que nossa explicação tem para nos dizer:

plot_image_explanation(clarification)

Podemos ver que o modelo, para as principais courses previstas, se concentra no gato, o que é bom, pois ambos são raças de gatos diferentes. A função da plotagem obteve algumas funções diferentes para ajudá -lo a ajustar o visible e filtra superpixels de baixa pontuação por padrão. Uma visão alternativa que coloca mais foco nos superpixels relevantes, mas take away o contexto pode ser visto usando
show = 'block':

plot_image_explanation(clarification, show = 'block', threshold = 0.01)

Embora não seja tão comum nas explicações da imagem, também é possível olhar para as áreas de uma imagem que contradiz a classe:

plot_image_explanation(clarification, threshold = 0, show_negative = TRUE, fill_alpha = 0.6)

Como cada explicação leva mais tempo para criar e precisa ser aprimorada por imagem, as explicações de imagem não são algo que você criará em lotes grandes, como pode fazer com dados tabulares e de texto. Ainda assim, algumas explicações podem permitir que você entenda melhor seu modelo e seja usado para comunicar o funcionamento do seu modelo. Além disso, como o fator limitador de tempo nas explicações de imagem são o classificador de imagem e não o próprio cal, ele é obrigado à medida que os classificadores de imagem se tornam mais executivos.

Recupere

Além do suporte de Keras e da imagem, foram adicionadas uma série de outros recursos e melhorias. Aqui está uma visão geral rápida:

  • Todas as parcelas de explicação agora incluem o ajuste da regressão de cume usada para fazer a explicação. Isso facilita a avaliação de quão boas as suposições sobre linearidade native são mantidas.
  • Ao explicar dados tabulares, a medida de distância padrão é agora 'gower'
    do gower pacote. gower torna possível medir distâncias entre dados heterogêneos sem converter todos os recursos em numéricos e experimentar diferentes grãos exponenciais.
  • Ao explicar, os recursos numéricos de dados tabulares não serão mais amostrados a partir de uma distribuição regular durante as permutações, mas a partir de uma densidade do kernel definida pelos dados de treinamento. Isso deve garantir que as permutações sejam mais representativas da entrada esperada.

Embrulhando

Este lançamento representa um marco importante para lime em R. com a adição de explicações de imagem, o lime O pacote está agora a par ou acima de seu parente python, em termos de recursos. Desenvolvimento adicional se concentrará em melhorar o desempenho do modelo, por exemplo, adicionando paralelismo ou melhorando a definição do modelo native, além de explorar tipos de explicação alternativos, como
âncora.

Feliz explicando!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *