O Blitz é uma biblioteca simples e extensível para criar camadas de rede neural bayesiana (com base no que é proposto na incerteza de peso no papel das redes neurais) no Pytorch. Usando camadas e utilitários de blitz, você pode adicionar incrustações e reunir o custo da complexidade do seu modelo de uma maneira simples que não afeta a interação entre suas camadas, como se você estivesse usando o Pytorch padrão.
Usando nossas classes de amostrador de peso central, você pode estender e melhorar essa biblioteca para adicionar incertenidade a um escopo maior de camadas, como desejará de uma maneira bem integrada à Pytorch. Também são bem -vindos os pedidos de tração.
Para instalar o blitz, você pode usar o comando pip:
pip install blitz-bayesian-pytorch
Ou, via conda:
conda install -c conda-forge blitz-bayesian-pytorch
Você também pode clonei-lo e instalá-lo localmente:
conda create -n blitz python=3.9
conda activate blitz
git clone https://github.com/piEsposito/blitz-bayesian-deep-learning.git
cd blitz-bayesian-deep-learning
pip install .
Documentação para nossas camadas, amostrador e utilitários de peso (e distribuição anterior):
(Você pode vê -lo por si mesmo executando este exemplo em sua máquina).
Agora veremos como o aprendizado profundo bayesiano pode ser usado para regressão para reunir intervalo de confiança em relação ao nosso projeto de dados, em vez de uma previsão pontual de valor contínuo. Reunir um intervalo de confiança para sua previsão pode ser uma informação ainda mais útil do que uma estimativa de baixo erro.
Eu sustento minha argumentação sobre o fato de que, com um intervalo de confiança bom/alto, você pode tomar uma decisão mais confiável do que com uma estimativa muito proximal em alguns contextos: se você está tentando obter lucro com uma operação de negociação, por exemplo, ter um bom intervalo de confiança pode levar você a saber se, pelo menos, o valor em que a operação será menor ou mais baixa.
Saber se um valor será, certamente (ou com boa probabilidade) em um intervalo determinado pode ajudar as pessoas a uma decisão sensata mais do que uma estimativa muito proximal de que, se menor ou maior que algum valor limite, pode causar perda em uma transação. O ponto é que, às vezes, saber se haverá lucro pode ser mais útil do que medi -lo.
Para demonstrar isso, criaremos um regressor da rede neural bayesiana para o conjunto de dados de brinquedos de dados de Boston-House, tentando criar intervalo de confiança (IC) para as casas das quais o preço que estamos tentando prever. Realizaremos alguma escala e o IC será de cerca de 75%. Será interessante ver que cerca de 90% dos CIs previstos são menores que o limite alto ou (inclusivo) mais alto que o mais baixo.
Apesar dos módulos conhecidos, traremos do Decorador Blitz Athe variational_estimator , o que nos ajuda a lidar com as camadas bayesianlinear no módulo, mantendo -o totalmente integrado ao restante da tocha e, é claro, BayesianLinear , que é a nossa camada que apresenta a incerteza do peso.
import torch
import torch . nn as nn
import torch . nn . functional as F
import torch . optim as optim
import numpy as np
from blitz . modules import BayesianLinear
from blitz . utils import variational_estimator
from sklearn . datasets import load_boston
from sklearn . preprocessing import StandardScaler
from sklearn . model_selection import train_test_split Nada de novo sob o sol aqui, estamos importando e escalando os dados para ajudar no treinamento.
X , y = load_boston ( return_X_y = True )
X = StandardScaler (). fit_transform ( X )
y = StandardScaler (). fit_transform ( np . expand_dims ( y , - 1 ))
X_train , X_test , y_train , y_test = train_test_split ( X ,
y ,
test_size = .25 ,
random_state = 42 )
X_train , y_train = torch . tensor ( X_train ). float (), torch . tensor ( y_train ). float ()
X_test , y_test = torch . tensor ( X_test ). float (), torch . tensor ( y_test ). float ()Podemos criar nossa classe com a ingregação do NN.Module, como faríamos com qualquer rede de tocha. Nosso decorador apresenta os métodos para lidar com as características bayesianas, como calculando o custo da complexidade das camadas bayesianas e fazendo muitos feedwards (amostrando pesos diferentes em cada um) para provar nossa perda.
@ variational_estimator
class BayesianRegressor ( nn . Module ):
def __init__ ( self , input_dim , output_dim ):
super (). __init__ ()
#self.linear = nn.Linear(input_dim, output_dim)
self . blinear1 = BayesianLinear ( input_dim , 512 )
self . blinear2 = BayesianLinear ( 512 , output_dim )
def forward ( self , x ):
x_ = self . blinear1 ( x )
x_ = F . relu ( x_ )
return self . blinear2 ( x_ )Esta função cria um intervalo de confiança para cada previsão no lote em que estamos tentando amostrar o valor do rótulo. Em seguida, podemos medir a precisão de nossas previsões, buscando quanto das distribuições de preditonia realmente incluíram o rótulo correto para o DataPoint.
def evaluate_regression ( regressor ,
X ,
y ,
samples = 100 ,
std_multiplier = 2 ):
preds = [ regressor ( X ) for i in range ( samples )]
preds = torch . stack ( preds )
means = preds . mean ( axis = 0 )
stds = preds . std ( axis = 0 )
ci_upper = means + ( std_multiplier * stds )
ci_lower = means - ( std_multiplier * stds )
ic_acc = ( ci_lower <= y ) * ( ci_upper >= y )
ic_acc = ic_acc . float (). mean ()
return ic_acc , ( ci_upper >= y ). float (). mean (), ( ci_lower <= y ). float (). mean () Observe aqui que criamos nosso BayesianRegressor , como faríamos com outras redes neurais.
regressor = BayesianRegressor ( 13 , 1 )
optimizer = optim . Adam ( regressor . parameters (), lr = 0.01 )
criterion = torch . nn . MSELoss ()
ds_train = torch . utils . data . TensorDataset ( X_train , y_train )
dataloader_train = torch . utils . data . DataLoader ( ds_train , batch_size = 16 , shuffle = True )
ds_test = torch . utils . data . TensorDataset ( X_test , y_test )
dataloader_test = torch . utils . data . DataLoader ( ds_test , batch_size = 16 , shuffle = True )Fazemos um ciclo de treinamento que difere apenas de um treinamento comum na tocha, tendo sua perda amostrada pelo método Sample_elbo. Todas as outras coisas podem ser feitas normalmente, pois nosso objetivo com Blitz é facilitar sua vida em iterar seus dados com diferentes NNs bayesianos sem problemas.
Aqui está o nosso ciclo de treinamento muito simples:
iteration = 0
for epoch in range ( 100 ):
for i , ( datapoints , labels ) in enumerate ( dataloader_train ):
optimizer . zero_grad ()
loss = regressor . sample_elbo ( inputs = datapoints ,
labels = labels ,
criterion = criterion ,
sample_nbr = 3 )
loss . backward ()
optimizer . step ()
iteration += 1
if iteration % 100 == 0 :
ic_acc , under_ci_upper , over_ci_lower = evaluate_regression ( regressor ,
X_test ,
y_test ,
samples = 25 ,
std_multiplier = 3 )
print ( "CI acc: {:.2f}, CI upper acc: {:.2f}, CI lower acc: {:.2f}" . format ( ic_acc , under_ci_upper , over_ci_lower ))
print ( "Loss: {:.4f}" . format ( loss ))Uma explicação muito rápida de como a incerteza é introduzida nas redes neurais bayesianas e como modelamos sua perda para melhorar objetivamente a confiança em relação à sua previsão e reduzir a variação sem abandonar.
Como sabemos, em camadas determinísticas da rede neural (não bayesiana), os parâmetros treináveis correspondem diretamente aos pesos usados em sua transformação linear do anterior (ou a entrada, se for o caso). Corresponde à seguinte equação:
(Z corresponde à saída ativada da camada i)
As camadas bayesianas buscam introduzir incerteza em seus pesos, amostrando -os de uma distribuição parametrizada por variáveis treináveis em cada operação de feedforward.
Isso permite não apenas otimizar as métricas de desempenho do modelo, mas também reunir a incerteza das previsões da rede em relação a um ponto de dados específico (amostrando -o muitas vezes e medindo a dispersão) e reduzindo o máximo possível a variação da rede sobre a previsão, tornando possível o que se conhecemos específico.
Para fazer isso, em cada operação de feedforward, amostramos os parâmetros da transformação linear com as seguintes equações (onde ρ parametrizam o desvio padrão e μ parametrizam a média para os parâmetros de transformação linear de amostras):
Para os pesos:
Onde o W amostrado corresponde aos pesos utilizados na transformação linear para a camada ITH na enésima amostra.
Para os preconceitos:
Onde o B amostrado corresponde aos vieses utilizados na transformação linear para a camada Ith na enésima amostra.
Mesmo difícil, temos um multiplicador aleatório para nossos pesos e vieses, é possível otimizá -los, dada alguma função diferenciável dos parâmetros amostrados e treináveis (no nosso caso, a perda), resumindo a derivada da função em relação a ambos:
Portanto:
e
Sabe -se que a perda de cruzentropia (e MSE) é diferenciável. Portanto, se provarmos que existe uma função de custo de complexidade que é diferenciável, podemos deixar para nossa estrutura levar os derivados e calcular os gradientes na etapa de otimização.
O custo da complexidade é calculado, na operação de feedforward, por cada uma das camadas bayesianas (com as camadas de distribuição do simpled-simplei predefinido e sua distribuição empírica). A soma do custo da complexidade de cada camada é somada para a perda.
Conforme proposto na incerteza de peso no artigo de redes neurais, podemos reunir o custo da complexidade de uma distribuição, levando a divergência de Kullback-Leibler a uma distribuição muito mais simples e, fazendo alguma aproximação, poderemos diferenciar essa função em relação às suas variáveis (as distribuições):
Seja um pdf de distribuição de baixa entropia definido à mão, que será assumido como uma distribuição "a priori" para os pesos
Seja o pdf de distribuição empírica a posteriori para nossos pesos amostrados, dados seus parâmetros.
Portanto, para cada escalar na matriz am amostrada:
Ao assumir um n muito grande, poderíamos aproximar:
e portanto:
Como o esperado (médio) da distribuição Q acaba apenas escalando os valores, podemos retirá-la da equação (pois não haverá raciocínio de estrutura). Tenha um custo de complexidade da enésima amostra como:
Que é diferenciável em relação a todos os seus parâmetros.
Portanto, toda a função de custo na enésima amostra de pesos será:
Podemos estimar a verdadeira função de custo total da amostragem de Monte Carlo (alimentando o Netwok x vezes e tomando a média por perda total) e depois retropropagação usando nosso valor estimado. Funciona para um baixo número de experimentos por backprop e até para experimentos unitários.
Chegamos ao fim de um aprendizado profundo bayesiano em poucas palavras. Ao saber o que está sendo feito aqui, você pode implementar seu modelo BNN como desejar.
Talvez você possa otimizar fazendo uma etapa otimizada por amostra ou usando esse método Monte-Carlo-ish para reunir a perda algumas vezes, pegue sua média e depois otimizador. Sua jogada.
FYI: Nossas camadas e utilitários bayesianos ajudam a calcular o custo da complexidade ao longo das camadas em cada operação de feedforward; portanto, não se importe muito.
Se você usar BLiTZ em sua pesquisa, poderá citar a seguinte:
@misc { esposito2020blitzbdl ,
author = { Piero Esposito } ,
title = { BLiTZ - Bayesian Layers in Torch Zoo (a Bayesian Deep Learing library for Torch) } ,
year = { 2020 } ,
publisher = { GitHub } ,
journal = { GitHub repository } ,
howpublished = { url{https://github.com/piEsposito/blitz-bayesian-deep-learning/} } ,
}