Cet article est un mémorandum sur la façon de définir un modèle par Pytorch, qui est l'un des cadres d'apprentissage en profondeur, comment apprendre et comment créer une fonction auto-conçue.
Pytorch peut être facilement installé avec conda ou pip. Si vous sélectionnez la version d'os, python ou cuda depuis ici, un script approprié sera affiché, vous pouvez donc l'installer en le copiant. (Cuda et cudnn doivent être configurés séparément) Actuellement, il semble que seuls linux ou osx soient pris en charge et que Windows ne soit pas pris en charge. (Depuis la version 0.4, Windows est également officiellement pris en charge.)
Si vous envisagez d'utiliser Pytorch à partir de maintenant, Tutoriels officiels est beaucoup plus facile à comprendre que cet article, vous devriez donc vous y référer. De plus, exemple est très utile. Autres Si vous regardez Documents et Forums, la plupart des choses que vous ne comprenez pas seront résolues.
Pytorch utilise essentiellement torch.Tensor pour les opérations matricielles. L'utilisation est fondamentalement la même que la torche.Tensor of Torch7. Cependant, contrairement à Torch7, l'entrée du modèle est basée sur l'entrée dans le mini lot. Dans le cas de la convolution 2D, il n'y avait aucun problème avec l'entrée 3D ou 4D dans Torch7, mais 4D est supposé dans Pytorch. De plus, lors du calcul à l'aide du modèle, changez torch.Tensor en Variable et utilisez-le. Dans Pytorch 0.4 ou version antérieure, si les éléments suivants sont importés, vous pouvez effectuer une définition et un entraînement de modèle de base.
import torch
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
Puisque Variable est intégré à torch.Tensor dans Pytorch 0.4 et versions ultérieures, il n'est pas nécessaire d'importer Variable.
Dans Pytorch, il est possible de définir un modèle de la même manière que Torch7 comme suit.
model1.
model = nn.Sequential()
model.add_module('fc1', nn.Linear(10,100))
model.add_module('relu', nn.ReLU())
model.add_module('fc2', nn.Linear(100,10))
La différence est que vous donnez un nom à chaque couche. Il est également possible de mettre un calque en liste et de plonger dans nn.Sequential ().
model2.py
layer = []
layer.append(nn.Linear(10,100))
layer.append(nn.ReLU())
layer.append(nn.Linear(100,10))
model = nn.Sequential(*layer)
Il peut également être défini comme une classe comme indiqué ci-dessous.
model3.py
import torch.nn.functional as F
class Model(nn.Module):
def __init__(self):
super(Model,self).__init__()
self.fc1 = nn.Linear(10,100)
self.fc2 = nn.Linear(100,10)
def forward(self,x):
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
return x
Si vous avez déjà utilisé le chainer, je pense que c'est une méthode de définition familière. Dans Pytorch, il est possible de réduire la quantité de définition de modèle et la quantité de description avant en utilisant pleinement nn.Sequential. Exemple d'implémentation du package torchvision de Pytorch peut être utilisé comme référence pour savoir comment construire un modèle. Nous allons construire un modèle en utilisant ou en combinant les trois ci-dessus selon le but.
L'une des erreurs les plus courantes que vous faites lorsque vous commencez à utiliser pytorch est de conserver les calques dans une liste. Lorsque vous utilisez plusieurs fois la même couche d'hyper-paramètres, il est plus facile de la définir à l'aide de l'instruction for, mais si vous conservez les paramètres apprenables dans une liste, vous pouvez les conserver dans la liste lorsque vous appelez les paramètres du modèle. Les paramètres de la couche ne sont pas reconnus comme paramètres et ne sont pas appelés. Ce qui se passe, c'est que les paramètres ne sont pas mis à jour pendant l'apprentissage. Ce qui suit est un mauvais exemple.
list.py
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.layer = [nn.Linear(10,10) for _ in range(10)]
def forward(self, x):
for i in range(len(self.layer)):
x = self.layer[i](x)
return x
model = Model()
# model.parameters()Vous pouvez obtenir l'itérateur du paramètre d'apprentissage avec
#Si vous le tenez dans la liste, vous ne pouvez pas obtenir les paramètres des modules dans la liste.
#optim sera décrit plus tard
optimize = optim.SGD(model.parameters(), lr=0.1)
Par conséquent, dans un tel cas, définissez-le en utilisant nn.ModuleList.
modulelist.py
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
layer = [nn.Linear(10,10) for _ in range(10)]
self.layer = nn.ModuleList(layer)
def forward(self, x):
for i in range(len(self.layer)):
x = self.layer[i](x)
return x
model = Model()
# model.parameters()Vous pouvez obtenir l'itérateur du paramètre d'apprentissage avec
#Si vous le tenez dans la liste, vous ne pouvez pas obtenir les paramètres des modules dans la liste.
#optim sera décrit plus tard
optimize = optim.SGD(model.parameters(), lr=0.1)
Soyez prudent car vous en êtes accro.
Il peut être calculé sur le GPU en définissant le modèle et les variables comme cudatensor comme indiqué ci-dessous.
gpu.py
import torch
x = torch.randn(10)
y = torch.randn(10)
"""
Pytorch 0.4 ou plus tôt
x = x.cuda()
y = y.cuda(0) #Si vous entrez un nombre dans l'argument, vous pouvez utiliser le GPU avec l'id correspondant au nombre
z = x * y #Le calcul se fait sur le GPU.
z = z.cpu() #au cpu
"""
"""
Pytorch 0.4 ou plus tard
x = x.to('cuda')
y = y.to('cuda:0') #Après cuda:Utilisez un GPU prenant en charge les nombres
z = x * y
z = z.to('cpu') #au cpu
"""
print(x.is_cuda) #Vrai si la variable est sur le GPU
Avec torchvision.models, Pytorch permet de définir facilement AlexNet, VGGNet, ResNet, DenseNet, SqueezeNet, GoogleNet et d'utiliser facilement ces modèles formés.
get_model.py
import torchvision.models as models
alexnet = models.alexnet()
pretrain_alexnet = models.alexnet(pretrained=True) #Les modèles formés peuvent être téléchargés en définissant l'option Pretrained sur True
De plus, un réglage fin peut être effectué en modifiant le nombre de dimensions de la sortie comme indiqué ci-dessous.
finetune1.py
resnet = models.resnet50(pretrained=True)
resnet.fc = nn.Liear(2048, 100)
De plus, si vous ne souhaitez utiliser que certaines couches, vous pouvez écrire comme suit.
finetune2.py
resnet = models.resnet50(pretrained=True)
resnet = nn.Sequential(*list(resnet.children())[:-3])
Il est possible de supprimer n'importe quel calque en utilisant des tranches. Voici un exemple de changement de Global Average Pooling de resnet en Max Pooling pour rendre la sortie en 10 dimensions.
resnet_finetune.py
class Resnet(nn.Module):
def __init__(self):
super(Resnet,self).__init__()
resnet = models.resnet50(pretrained=True)
self.resnet = nn.Sequential(*list(resnet.children())[:-2])
self.maxpool = nn.MaxPool2d(kernel_size=7)
self.fc = nn.Linear(2048, 10)
def forward(self,x):
x = self.resnet(x)
x = self.maxpool(x)
x = self.fc(x)
return x
Utilisez le package optim pour mettre à jour les paramètres à l'aide de n'importe quelle technique d'optimisation. Après avoir défini les paramètres de la méthode d'optimisation, vous pouvez mettre à jour en appelant step () chaque fois que vous effectuez un calcul en arrière.
update.py
"""
Pytorch 0.4 ou plus tard
if torch.cuda.is_available(): #Vérifiez si le GPU est disponible
device = 'cuda'
else:
device = 'cpu'
"""
#définition du modèle
model = models.resnet18()
"""
Pytorch 0.4 ou plus tard
model = model.to(device)
"""
#Paramétrage de la méthode d'optimisation
optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
#Définition de la fonction de perte
criterion = nn.MSELoss()
#Générer une entrée et une réponse correcte avec des nombres aléatoires
input = torch.randn(1,3,224,224) #Lot x canal x hauteur x largeur
target = torch.randn(1,1000)
"""
# Pytorch 0.4 ou plus tôt
#Changer en variable et calculer
input = Variable(input)
"""
# requires_En définissant grad sur False, il est possible de ne pas calculer le gradient pour cette variable.
#Il est défini sur False par défaut, il n'est donc pas nécessaire de l'écrire explicitement comme cette fois.
"""
Pytorch 0.4 ou plus tôt
target = Variable(target, requires_grad=False)
"""
"""
Pytorch 0.4 ou plus tard
target.requires_grad = False
"""
#Le comportement des modules qui se comportent différemment pendant l'apprentissage et l'inférence, comme la normalisation par lots, peut être changé en comportement pendant l'apprentissage.
# model.eval()Peut être modifié pour le comportement au moment de l'inférence avec
model.train()
#Boucle d'apprentissage
for i in range(100):
#Propagation vers l'avant
out = model(input)
#Calcul des pertes
loss = criterion(out, target)
#Initialisation du gradient
optimizer.zero_grad()
#Calcul du gradient
loss.backward()
#Mise à jour des paramètres
optimizer.step()
Si vous n'utilisez pas optim, vous pouvez mettre à jour les paramètres en réécrivant la partie de optimizer.step () comme suit.
update_without_optim.py
for param in model.parameters():
param.data -= learning_rate * param.grad.data
Dans le cas du RNN, il est possible d'effectuer une rétro-propagation en ajoutant la perte comme indiqué ci-dessous et en appelant l'étape () au moment de la mise à jour.
rnn_update.py
class RNN(nn.Module):
def __init__(self, data_size, hidden_size, output_size):
super(RNN, self).__init__()
self.hidden_size = hidden_size
input_size = data_size + hidden_size
self.i2h = nn.Linear(input_size, hidden_size)
self.h2o = nn.Linear(hidden_size, output_size)
def forward(self, data, last_hidden):
input = torch.cat((data, last_hidden), 1)
hidden = self.i2h(input)
output = self.h2o(hidden)
return hidden, output
RNN = RNN()
#Omis comme l'optimiseur
for i in range(10):
hidden, output = RNN(input, hidden)
loss += criterion(output, target)
loss.backward()
optimizer.step()
Dans le code ci-dessus, la perte est calculée et les paramètres sont mis à jour après 10 étapes.
Fondamentalement, il peut être sauvegardé avec torch.save () comme Torch7, mais seuls les paramètres d'apprentissage sont sauvegardés avec state_dict ().
model_save.py
model = models.resnet50(pretrained=True)
#Enregistrer le modèle
torch.save(model.state_dict(), 'weight.pth')
model2 = models.resnet50()
#Paramètres de charge
param = torch.load('weight.pth')
model2.load_state_dict(param)
Non seulement le modèle mais aussi l'optimiseur peuvent être enregistrés en utilisant torch.save () et state_dict ().
optimizer_save.py
optimizer = optim.SGD(model.parameters(), lr=0.1)
torch.save(optimizer.state_dict(), 'optimizer.pth')
optimizer2 = optim.SGD(model.parameters(), lr=0.1)
optimizer2.load_state_dict(torch.load('optimizer.pth')
Pytorch peut facilement créer des couches et des fonctions originales à l'aide de Numpy. (Vous pouvez également écrire en C) Héritez simplement de la classe Function et écrivez des calculs avant et arrière. Par exemple, si vous créez votre propre fonction ReLU, ce sera comme suit. (La fonction relu est implémentée à l'origine, vous n'avez donc pas à l'écrire vous-même)
relu.py
from torch.autograd import Function
class relu(Function):
def forward(self,x):
# torch.De Tensor à Numpy
numpy_x = x.numpy()
result = np.maximum(numpy_x,0)
#de la torche à la torche.Vers Tensor
result = torch.FloatTensor(result)
#Hold Tensor pour le calcul en arrière
self.save_for_backward(result)
return result
def backward(self, grad_output):
result = self.saved_tensors[0]
grad_input = grad_output.numpy() * (result.numpy() > 0)
#Renvoie le dégradé par rapport à l'entrée
return torch.FloatTensor(grad_input)
S'il y a un paramètre à apprendre, il est nécessaire de mordre Parameter dans le paramètre d'apprentissage et de définir une classe qui hérite de nn.Module. Implémentez une opération qui pondère uniquement l'entrée à titre d'exemple. (Lorsque x = [1,2,3], w = [0,1,0,2,0,3], la sortie est [0,1,0,4,0,9]. W est un paramètre apprenable)
elemwise.py
from torch.autograd import Function
from torch.nn.parameter import Parameter
class elemwiseFunction(Function):
def forward(self, x, w):
self.save_for_backward(x, w)
numpy_x = x.numpy()
numpy_w = w.numpy()
result = numpy_x*numpy_w
return torch.FloatTensor(result)
def backward(self, grad_output):
input, w = self.saved_tensors
w_grad = input.numpy() * grad_output
x_grad = w.numpy() * grad_output
#Renvoie le gradient pour l'entrée et le gradient pour l'apprentissage des paramètres
return torch.FloatTensor(x_grad), torch.FloatTensor(w_grad)
class elemwise(nn.Module):
def __init__(self):
super(elemwise,self).__init__()
self.w = Parameter(torch.randn(10)
def forward(self):
return elemwiseFunction()(x, self.w)
Le code ci-dessus n'a pas besoin d'utiliser numpy car l'entrée et la sortie de l'avant et de l'arrière doivent être torch.Tensor. Par exemple, vous pouvez écrire en utilisant cupy, ou vous pouvez écrire en utilisant la théorie extrême ou d'autres bibliothèques. De plus, puisque Pytorch a une différenciation automatique, il n'est pas nécessaire d'écrire à l'envers en complétant le calcul de torch.Tensor sans utiliser numpy dans le calcul ci-dessus.
elemwise_without_backward.py
class elemwise(nn.Module):
def __init__(self):
super(elemwise,self).__init__()
self.w = Parameter(torch.randn(10)
def forward(self, x):
return x * self.w
Ce qui suit est un site de référence pour savoir comment définir vos propres couches en C ou C ++ et comment définir des fonctions à l'aide du noyau CUDA. Tutoriel officiel sur la façon d'étendre avec C, Mise en œuvre officielle avec C, Exemple d'implémentation officiel en C ++, Tutoriel officiel sur l'extension avec C ++ et noyau cuda ), Code source de Pytorch Dans le code source, TH ,, THS, THC et THCS ont des implémentations liées à torch.Tensor, et THNN et THCUNN ont des implémentations liées aux réseaux neuronaux.
Vous trouverez ci-dessous quelques conseils pour vous aider à apprendre NN. Je voudrais l'ajouter à chaque fois que je me souviens de quelque chose.
pytorch effectue des calculs tout en construisant un graphe de calcul pour la marche arrière pendant la propagation vers l'avant. Cela n'est pas nécessaire pendant l'inférence et il est recommandé de l'arrêter comme suit pour économiser de la mémoire.
no_grad.py
import torch
x = torch.randn(10)
"""
Pytorch 0.4 ou plus tôt
"""
from torch.autograd import Variable
x = Variable(x, volatile=True) #Définissez l'option volatile sur True
y = x**2
"""
Pytorch 0.4 ou plus tard
"""
with torch.no_grad():
#Exécutez les opérations dans lesquelles vous ne souhaitez pas créer de graphique de calcul avec
y = x**2
Vous voudrez peut-être calculer la perte moyenne pour voir le processus d'apprentissage. À ce moment-là, si vous additionnez simplement les pertes de chaque itération, il y a un problème que le graphe de calcul continuera à être créé et la mémoire sera consommée, alors décrivez comme suit.
mean_loss.py
sum_loss = 0
#Boucle d'apprentissage
for i in range(100):
"""
Description telle que la propagation vers l'avant
"""
loss = loss_function(outputs, targets) #Calculer la perte avec une fonction de perte appropriée
"""
Pytorch 0.4 ou plus tôt
"""
#Variable à torcher par données.À Tensor. De plus, passez de gpu à cpu et spécifiez le 0ème index.
sum_loss += loss.data.cpu()[0]
"""
Pytorch 0.4 ou plus tard
"""
sum_loss += loss.item()
print("mean loss: ", sum_loss/i)
L'opération était gênante avant Pytorch 0.4, mais elle est devenue plus simple en appelant item () après 0.4.
Recommended Posts