1. Введение
Байесовские нейронные сети отличаются от обычных нейронных сетей тем, что их весовые параметры являются случайными величинами, а не детерминированными значениями. Как показано ниже:
То есть, в отличие от традиционных нейронных сетей, которые используют функции потерь, такие как перекрестная энтропия и mse, для соответствия значениям меток, байесовские нейронные сети соответствуют апостериорному распределению.
Преимущество этого заключается в уменьшении переобучения.
2. Модель БНН
В отличие от DNN, BNN может изучать распределение предсказания, не только может давать предсказанное значение, но также можетдают неопределенность прогноза. Это очень важно для многих проблем, таких как: знаменитая проблема исследования и эксплуатации (EE) в машинном обучении, в задачах обучения с подкреплением, нужно ли агенту использовать существующие знания для принятия решений или попробовать что-то неизвестное, экспериментальный дизайн. , байесовская оптимизация используется для настройки гиперпараметров, а следующая точка выбирается на основе оптимального значения текущей модели или путем исследования некоторого пространства с высокой неопределенностью. Например:Обнаружение аномального образца, такие задачи, как обнаружение состязательной выборки, из-за способности количественной оценки неопределенности BNN, он имеет очень сильнуюпрочность.
Вероятностное моделирование:
Здесь сопряженное распределение распределения правдоподобия выбирается таким образом, чтобы апостериорное значение можно было вычислить аналитически.
Например, априорное значение бета-распределения и вероятность распределения Бернулли приведут к апостериорному значению, которое подчиняется бета-распределению.
Из-за сопряженного распределения априорное распределение должно быть ограничено. Поэтому мы пытаемся аппроксимировать апостериорное распределение с помощью принятия и вариационного вывода.
Нейронные сети:Использование полностью подключенной сети для соответствия данным эквивалентно использованию нескольких полностью подключенных сетей. Однако нейронная сеть склонна к переобучению и имеет плохую генерализацию, что не может дать уверенности в прогнозируемых результатах.
BNN:Объедините вероятностное моделирование с нейронными сетями и будьте уверены в прогнозируемых результатах.
Априоры используются для описания ключевых параметров и служат входными данными для нейронной сети. Выход нейронной сети используется для описания вероятности определенного распределения вероятностей. Апостериорное распределение вычисляется путем выборки или вариационного вывода. При этом, в отличие от нейронных сетей, вес W является уже не определенной величиной, а распределением вероятностей.
BNN моделируется следующим образом:
Предположим, что сетевые параметры NN равны,- априорное распределение параметров с учетом наблюдаемых данных,здесьвходные данные,это данные этикетки. BNN рассчитывает дать следующее распределение:
То есть наше прогнозируемое значение равно:
так как,является случайной величиной, поэтому наше предсказанное значение также является случайной величиной.
в:
здесьэто апостериорное распределение,функция правдоподобия,является предельной вероятностью.
Из формулы (1) видно, что суть использования BNN для вероятностного моделирования и прогнозирования данных состоит в том, чтобы сделать эффективный приблизительный апостериорный вывод, и вариационный вывод VI или выборка являются очень подходящим методом.
Если выборка: Мы выбираем апостериорное распределениеоценить, рассчитанный для каждого образца, где f — наша нейронная сеть.
Это наш результат, который представляет собой распределение, а не значение, по которому мы можем оценить неопределенность наших прогнозов.
3. Обучение BNN на основе вариационного вывода
Если вы напрямую выбираете апостериорную вероятностьоценитьЕсли , существует многомерная проблема апостериорного распределения, и идея вариационного вывода состоит в том, чтобы использовать простое распределение для аппроксимации апостериорного распределения.
выражать, каждый весиз нормального распределениясредняя выборка.
надеятьсяипохожи, и используйте дивергенцию KL для измерения расстояния между двумя распределениями. То есть оптимизировать:
Дальнейшее происхождение:
формула,Представляет распределение весовых параметров с учетом параметров нормального распределения;Представляет вероятность наблюдаемых данных после заданных параметров сети;Представляет априорные значения весов, которые можно использовать в качестве регуляризации модели.
и использовать
для представления вариационной нижней границы ELBO, то есть формула (4) эквивалентна максимизации ELBO:
в,
Нам нужно вывести математическое ожидание в уравнении (4), но здесь мы используем прием перепараметризации весов:
в,.
Итак, используйтезаменятьс последующим:
То есть мы можем передать несколько разных, спроситьдля аппроксимации пары расхождений KLруководство.
Кроме того, кромеВ дополнение к повторной выборке, чтобы обеспечитьДиапазон значений параметра включает эту реальную ось, дляДля повторной выборки мы можем сделать,
Потом,,здесьбыло определено как изначальноНе то же самое.
4. Практика БНН
алгоритм:
- отпробовать, получить;
- Рассчитать отдельно,,.
Среди них расчетфактический расчет, . также получить. 3. Неоднократно обновлять параметры.
Реализация Питорча:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.distributions import Normal
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt
class Linear_BBB(nn.Module):
"""
Layer of our BNN.
"""
def __init__(self, input_features, output_features, prior_var=1.):
"""
Initialization of our layer : our prior is a normal distribution
centered in 0 and of variance 20.
"""
# initialize layers
super().__init__()
# set input and output dimensions
self.input_features = input_features
self.output_features = output_features
# initialize mu and rho parameters for the weights of the layer
self.w_mu = nn.Parameter(torch.zeros(output_features, input_features))
self.w_rho = nn.Parameter(torch.zeros(output_features, input_features))
#initialize mu and rho parameters for the layer's bias
self.b_mu = nn.Parameter(torch.zeros(output_features))
self.b_rho = nn.Parameter(torch.zeros(output_features))
#initialize weight samples (these will be calculated whenever the layer makes a prediction)
self.w = None
self.b = None
# initialize prior distribution for all of the weights and biases
self.prior = torch.distributions.Normal(0,prior_var)
def forward(self, input):
"""
Optimization process
"""
# sample weights
w_epsilon = Normal(0,1).sample(self.w_mu.shape)
self.w = self.w_mu + torch.log(1+torch.exp(self.w_rho)) * w_epsilon
# sample bias
b_epsilon = Normal(0,1).sample(self.b_mu.shape)
self.b = self.b_mu + torch.log(1+torch.exp(self.b_rho)) * b_epsilon
# record log prior by evaluating log pdf of prior at sampled weight and bias
w_log_prior = self.prior.log_prob(self.w)
b_log_prior = self.prior.log_prob(self.b)
self.log_prior = torch.sum(w_log_prior) + torch.sum(b_log_prior)
# record log variational posterior by evaluating log pdf of normal distribution defined by parameters with respect at the sampled values
self.w_post = Normal(self.w_mu.data, torch.log(1+torch.exp(self.w_rho)))
self.b_post = Normal(self.b_mu.data, torch.log(1+torch.exp(self.b_rho)))
self.log_post = self.w_post.log_prob(self.w).sum() + self.b_post.log_prob(self.b).sum()
return F.linear(input, self.w, self.b)
class MLP_BBB(nn.Module):
def __init__(self, hidden_units, noise_tol=.1, prior_var=1.):
# initialize the network like you would with a standard multilayer perceptron, but using the BBB layer
super().__init__()
self.hidden = Linear_BBB(1,hidden_units, prior_var=prior_var)
self.out = Linear_BBB(hidden_units, 1, prior_var=prior_var)
self.noise_tol = noise_tol # we will use the noise tolerance to calculate our likelihood
def forward(self, x):
# again, this is equivalent to a standard multilayer perceptron
x = torch.sigmoid(self.hidden(x))
x = self.out(x)
return x
def log_prior(self):
# calculate the log prior over all the layers
return self.hidden.log_prior + self.out.log_prior
def log_post(self):
# calculate the log posterior over all the layers
return self.hidden.log_post + self.out.log_post
def sample_elbo(self, input, target, samples):
# we calculate the negative elbo, which will be our loss function
#initialize tensors
outputs = torch.zeros(samples, target.shape[0])
log_priors = torch.zeros(samples)
log_posts = torch.zeros(samples)
log_likes = torch.zeros(samples)
# make predictions and calculate prior, posterior, and likelihood for a given number of samples
for i in range(samples):
outputs[i] = self(input).reshape(-1) # make predictions
log_priors[i] = self.log_prior() # get log prior
log_posts[i] = self.log_post() # get log variational posterior
log_likes[i] = Normal(outputs[i], self.noise_tol).log_prob(target.reshape(-1)).sum() # calculate the log likelihood
# calculate monte carlo estimate of prior posterior and likelihood
log_prior = log_priors.mean()
log_post = log_posts.mean()
log_like = log_likes.mean()
# calculate the negative elbo (which is our loss function)
loss = log_post - log_prior - log_like
return loss
def toy_function(x):
return -x**4 + 3*x**2 + 1
# toy dataset we can start with
x = torch.tensor([-2, -1.8, -1, 1, 1.8, 2]).reshape(-1,1)
y = toy_function(x)
net = MLP_BBB(32, prior_var=10)
optimizer = optim.Adam(net.parameters(), lr=.1)
epochs = 2000
for epoch in range(epochs): # loop over the dataset multiple times
optimizer.zero_grad()
# forward + backward + optimize
loss = net.sample_elbo(x, y, 1)
loss.backward()
optimizer.step()
if epoch % 10 == 0:
print('epoch: {}/{}'.format(epoch+1,epochs))
print('Loss:', loss.item())
print('Finished Training')
# samples is the number of "predictions" we make for 1 x-value.
samples = 100
x_tmp = torch.linspace(-5,5,100).reshape(-1,1)
y_samp = np.zeros((samples,100))
for s in range(samples):
y_tmp = net(x_tmp).detach().numpy()
y_samp[s] = y_tmp.reshape(-1)
plt.plot(x_tmp.numpy(), np.mean(y_samp, axis = 0), label='Mean Posterior Predictive')
plt.fill_between(x_tmp.numpy().reshape(-1), np.percentile(y_samp, 2.5, axis = 0), np.percentile(y_samp, 97.5, axis = 0), alpha = 0.25, label='95% Confidence')
plt.legend()
plt.scatter(x, toy_function(x))
plt.title('Posterior Predictive')
plt.show()
Вот среднее значение для 100 повторений и 97,5% больших и 2,5% малых линейных графиков из 100 средних (т.е. 95% достоверность).
Ссылаться на: