Le Deep Kernel Learning est une combinaison d'apprentissage profond et de processus gaussiens, et fait partie de l'apprentissage profond bayésien. En tant que méthode, un noyau profond est créé en utilisant les fonctionnalités produites par le réseau neuronal profond (DNN) comme entrée du noyau dans le processus gaussien. La formule est la suivante.
k_{deep}(x,x') = k(f(x),f(x'))
Puisque le processus gaussien équivaut à un réseau de neurones avec des unités infinies, il semble qu'il a été ajouté à la fin de DNN. Comme je l'ai essayé dans l'article précédent (https://qiita.com/takeajioka/items/f24d58d2b13017ab2b18), il est important d'optimiser les hyperparamètres du noyau lors du processus gaussien. L'apprentissage profond du noyau semble optimiser et apprendre les paramètres DNN et les hyper paramètres du noyau en même temps.
Veuillez vous référer au document suivant pour plus de détails. [1] Deep Kernel Learning, 2015, Andrew G. Wilson et al.,https://arxiv.org/abs/1511.02222 [2] Stochastic Variational Deep Kernel Learning, 2016, Andrew G. Wilson et al., https://arxiv.org/abs/1611.00336
Dans Pyro, vous pouvez facilement créer un noyau profond en utilisant la classe gp.kernels.Warping. Il existe un code pour l'apprentissage profond du noyau dans le tutoriel officiel de Pyro, apprenons donc en y faisant référence.
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from torchvision import transforms
import pyro
import pyro.contrib.gp as gp
import pyro.infer as infer
Puisque MNIST a une grande quantité de données, nous l'apprendrons dans un mini-lot. Définissez le jeu de données.
batch_size = 100
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, ), (0.5, ))])
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True)
testset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False)
Tout d'abord, préparez un modèle DNN normal.
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2(x), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
Enveloppez le noyau dans ceci pour créer un noyau profond.
rbf = gp.kernels.RBF(input_dim=10, lengthscale=torch.ones(10))
deep_kernel = gp.kernels.Warping(rbf, iwarping_fn=CNN())
Une approximation parcimonieuse est utilisée pour réduire le coût de calcul du processus gaussien. Dans l'approximation clairsemée, le point d'induction est utilisé, mais cette fois, nous utiliserons les données d'apprentissage pour une taille de lot.
Xu, _ = next(iter(trainloader))
likelihood = gp.likelihoods.MultiClass(num_classes=10)
gpmodule = gp.models.VariationalSparseGP(X=None, y=None, kernel=deep_kernel, Xu=Xu, likelihood=likelihood, latent_shape=torch.Size([10]), num_data=60000)
optimizer = torch.optim.Adam(gpmodule.parameters(), lr=0.01)
elbo = infer.TraceMeanField_ELBO()
loss_fn = elbo.differentiable_loss
Définit une fonction pour l'apprentissage par mini-lots.
def train(train_loader, gpmodule, optimizer, loss_fn, epoch):
total_loss = 0
for data, target in train_loader:
gpmodule.set_data(data, target)
optimizer.zero_grad()
loss = loss_fn(gpmodule.model, gpmodule.guide)
loss.backward()
optimizer.step()
total_loss += loss
return total_loss / len(train_loader)
def test(test_loader, gpmodule):
correct = 0
for data, target in test_loader:
f_loc, f_var = gpmodule(data)
pred = gpmodule.likelihood(f_loc, f_var)
correct += pred.eq(target).long().sum().item()
return 100. * correct / len(test_loader.dataset)
Apprenez.
import time
losses = []
accuracy = []
epochs = 10
for epoch in range(epochs):
start_time = time.time()
loss = train(trainloader, gpmodule, optimizer, loss_fn, epoch)
losses.append(loss)
with torch.no_grad():
acc = test(testloader, gpmodule)
accuracy.append(acc)
print("Amount of time spent for epoch {}: {}s\n".format(epoch+1, int(time.time() - start_time)))
print("loss:{:.2f}, accuracy:{}".format(losses[-1],accuracy[-1]))
J'ai pu apprendre une époque en 30 secondes environ. La précision finale était de 96,23%. (Il semble que cela puisse atteindre 99,41% dans le tutoriel officiel.) Affichez la courbe d'apprentissage.
import matplotlib.pyplot as plt
plt.subplot(2,1,1)
plt.plot(losses)
plt.xlabel("epoch")
plt.ylabel("loss")
plt.subplot(2,1,2)
plt.plot(accuracy)
plt.xlabel("epoch")
plt.ylabel("accuracy")
Regardons l'image de test et la sortie prévue côte à côte.
data, target = next(iter(testloader))
f_loc, f_var = gpmodule(data)
pred = gpmodule.likelihood(f_loc, f_var)
for i in range(len(data)):
plt.subplot(1,2,1)
plt.imshow(data[i].reshape(28, 28))
plt.subplot(1,2,2)
plt.bar(range(10), f_loc[:,i].detach(), yerr= f_var[:,i].detach())
ax = plt.gca()
ax.set_xticks(range(10))
plt.xlabel("class")
plt.savefig('image/figure'+ str(i) +'.png')
plt.clf()
La barre bleue est la valeur moyenne et la barre d'erreur est la distribution. On a constaté que chacune des 10 classes avait une sortie et que la classe correcte avait une valeur élevée.
Regardons également des images difficiles à distinguer. La sortie est élevée dans plusieurs classes. Compte tenu de la barre d'erreur, il semble qu'il n'y ait pas de différence significative.
J'ai pu apprendre comme un Deep Learning normal. Je pense que c'est un avantage que Deep Learnig normal n'a pas de pouvoir afficher la valeur moyenne et la variance en sortie. Il existe également un processus gaussien profond (DGP), qui est une pile de processus gaussiens, alors j'aimerais également étudier cela.
Recommended Posts