BLiTZ — A Bayesian Neural Network 해보기

2020. 4. 11. 22:14ML(머신러닝)/Bayesian

728x90

광고 한 번씩 눌러주세요! 블로그 운영에 큰 힘이 됩니다 :)

현재 일반적인 딥러닝 모델들엔 결과들은 결과에 대한 불확실성을 측정할 수가 없다. 
개인적으로 리스크 관리나 결과에 대한 신뢰도 측면에서 이점은 굉장히 중요하다 생각을 하고 있다.
하지만 베이지안과 뉴럴 네트워크를 결합하는 순간 굉장히 코드가 어려워지는 것을 느낀다.
그래도 평소에 베이지안 뉴럴 네트워크에 대해 관심을 가지고 있어서, 쉽게 할 수 있는 것이 있으면 해 보려고 노력하고 있다. 그러던 와중에 해당 패키지를 알게 됐다.  파이토치로 만든 패키지이다. 

베이지안 딥러닝은 determinstic 가중치를 가지지 않고, 가중치의 정규 분포로부터 가중치를 샘플링하는 방법론을 사용한다. 

일반적인 determinstic한 것은 다음과 같다. 여기서 보면 알 수 있듯이 W가 고정되어 있다.

Deterministic “vanilla” neural network feed-forward operation.

베이지안 딥러닝을 사용하게 되면, 가중치 분포에서 샘플링을 통해서 가중치를 가져와서 결과를 구한다.
그러므로 가중치 분포에 대한 샘플링으로 결괏값에 대한 불확실성을 측정할 수 있게 된다는 것이다.

해당 패키지 BLiTZ에서는 가중치에서 샘플링하는 방법으로 베이지안 딥러닝 패키지를 쓸 수 있게 했다.

import torch
from torch import nn
from blitz.modules import BayesianLinear

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, 64)
        self.blinear2 = BayesianLinear(64, output_dim)
        
    def forward(self, x):
        x_ = self.linear(x)
        x_ = self.blinear1(x_)
        return self.blinear2(x_)

텐서플로우를 주로 사용하지만, 파이토치는 참 쉽게 구현이 되는 것 같아서 그냥 봐도 느낌이 오니 참 좋은 것 같다.

Loss calculation

기존 논문 Weigh Uncertainty in Neural Networks에서 제안했듯이, 베이지안 뉴럴 네트워크의 비용 함수는 
cmplexity cost와 fitting-to-data cost의 결합으로 되어있다. 사실 아직 논문은 읽지 않는 상태라서 머라 할 말이 없다! ㅎ

복잡성 비용($P(W$)은 훨씬 단순하고 사전 정의된 pdf 함수에 상대적인 샘플링된 가중치(네트워크 상의 각 베이지안 층)의 확률 밀도 함수의 합으로 구성된다. 이렇게 함으로써, 최적화하는 동안 예측에 대한 모델의 분산은 줄어들 것이라고 말한다. 

위의 비용 함수를 사용하기 위해서는 variational_estimator decorator를 사용하면 된다. 

@variational_estimator
class BayesianRegressor(nn.Module):
    def __init__(self,):
        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)
        return self.blinear2(x_)
      
reg = BayesianRegressor()
preds = reg(dataset)
fit_loss = criterion(preds, labels)
complexity_loss = reg.nn_kl_divergence()

true_loss = fit_loss + complecity_loss

학습도 아주 심플하게 할 수 있다 ㄷㄷㄷ

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

Defining a confidence interval evaluating function

이 부분이 불확실성을 측정하는 함수 부분인데, 같은 X에 대해서 여러번의 샘플링을 통해서 여러 개를 생성하고 그것을 통해 평균과 표준 편차를 구해서 신뢰구간을 구한다.

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

아주 심플하다는 것을 알게 됬고, 분류 문제를 한번 변행 해서 해봤다.

 

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

import sklearn.datasets
import torch
import numpy as np

np.random.seed(0)
X, y = sklearn.datasets.make_moons(500,noise=0.2)

데이터 준비

X_train, X_test, y_train, y_test = train_test_split(X,
                                                    y,
                                                    test_size=.25,
                                                    random_state=42)

X_train, y_train= torch.from_numpy(X_train).type(torch.FloatTensor)  , torch.from_numpy(y_train).type(torch.LongTensor)
X_test, y_test= torch.from_numpy(X_test).type(torch.FloatTensor)  , torch.from_numpy(y_test).type(torch.LongTensor)

데이터셋 준비

ds_train = torch.utils.data.TensorDataset(X_train, y_train)
train_loader = torch.utils.data.DataLoader(ds_train, batch_size=16, shuffle=True)

ds_test = torch.utils.data.TensorDataset(X_test, y_test)
test_loader = torch.utils.data.DataLoader(ds_test, batch_size=16, shuffle=True)

아키텍처 코드

@variational_estimator
class BayesianNetwork(nn.Module):
    def __init__(self, input_dim, output_dim):
        super().__init__()
        self.blinear1 = BayesianLinear(input_dim, 15)
        self.blinear2 = BayesianLinear(15, 10)
        self.blinear3 = BayesianLinear(10, output_dim)
        
    def forward(self, x):
        x_ = self.blinear1(x)
        x_ = self.blinear2(x_)
        return self.blinear3(x_)

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
classifier = BayesianNetwork(X.shape[1], 2).to(device)
optimizer = optim.SGD(classifier.parameters(), lr=0.0005)
criterion = torch.nn.CrossEntropyLoss()

시각화 코드

import seaborn as sns
from sklearn.metrics import roc_auc_score
def evaluate_classification(classifier,X,y,
                            samples = 100, iteration=None):
    preds = [F.softmax(classifier(X),dim=1).numpy()[:,1] for i in range(samples)]
    dd = pd.DataFrame(preds).T
    d = pd.melt(dd.sample(300))
    auc = roc_auc_score(y.numpy(),
                        pd.DataFrame(preds).mean(axis=0).values)
    d.columns = ["index", "prob"]
    fig , ax = plt.subplots(figsize= (12,7))
    ax = sns.lineplot(x="index", y="prob", ci = 95 , 
                      markers=False, dashes=False, data=d )
    plt.title(f"iter : {iteration}, AUC : {auc*100:.2f}%", fontsize=15)
    plt.savefig(f"{visPath}/vis_{iteration:04d}.png")
    plt.close()

학습 코드

iteration = 0
for epoch in range(1000):
    for i, (datapoints, labels) in enumerate(train_loader):
        optimizer.zero_grad()
        loss = classifier.sample_elbo(inputs=datapoints.to(device),
                           labels=labels.to(device),
                           criterion=criterion,
                           sample_nbr=3)
        loss.backward()
        optimizer.step()
        
        iteration += 1
        if iteration%100==0:
            correct = 0
            total = 0
            with torch.no_grad():
                for data in test_loader:
                    data, labels = data
                    outputs = classifier(data.to(device))
                    _, predicted = torch.max(outputs.data, 1)
                    total += labels.size(0)
                    correct += (predicted == labels.to(device)).sum().item()
                print('Iteration: {} | Accuracy of the network on the 10000 test data: {} %'.format(str(iteration) ,str(100 * correct / total)))
                evaluate_classification(classifier,X_test.to(device),
                                        y_test.to(device),
                                        samples=40, iteration = iteration)

 

 

 

https://arxiv.org/abs/1505.05424

 

Weight Uncertainty in Neural Networks

We introduce a new, efficient, principled and backpropagation-compatible algorithm for learning a probability distribution on the weights of a neural network, called Bayes by Backprop. It regularises the weights by minimising a compression cost, known as t

arxiv.org

https://github.com/piEsposito/blitz-bayesian-deep-learning#Bayesian-Deep-Learning-in-a-Nutshell

 

piEsposito/blitz-bayesian-deep-learning

A simple and extensible library to create Bayesian Neural Network layers on PyTorch. - piEsposito/blitz-bayesian-deep-learning

github.com

https://towardsdatascience.com/blitz-a-bayesian-neural-network-library-for-pytorch-82f9998916c7

 

BLiTZ — A Bayesian Neural Network library for PyTorch

Blitz — Bayesian Layers in Torch Zoo is a simple and extensible library to create Bayesian Neural Network layers on the top of PyTorch.

towardsdatascience.com

 

728x90