2020/1/27 Publié
De nos jours, la recherche principale sur l'apprentissage automatique se fait dans le langage python, car python dispose de nombreuses bibliothèques (appelées modules) pour l'analyse et le calcul de données à grande vitesse. Parmi eux, nous utiliserons cette fois un module appelé ** pyTorch ** pour expliquer comment la différenciation automatique est effectuée et ce qui peut et ne peut pas être fait.
Cependant, cet article est comme votre propre mémo, et je souhaite que vous l'utilisiez comme référence uniquement, et il se peut que vous utilisiez des expressions ou des phrases incorrectes par souci de concision, mais comprenez cela. Je veux que tu le fasses.
De plus, dans cet article, nous n'apprendrons pas réellement à utiliser Network. Si cela vous intéresse, veuillez vous référer au lien ci-dessous.
Explication approfondie des CNN avec pyTorch
Si vous utilisez pyTorch pour la première fois, vous devez l'installer avec cmd car pyTorch n'est pas encore installé en python. Allez au lien ci-dessous, sélectionnez celui de votre environnement avec "QUICK START LOCALLY" en bas de la page, et entrez la commande qui apparaît avec cmd etc. (Vous devriez pouvoir copier et exécuter la commande).
Tout comme numpy a un type appelé ndarray, pyTorch a un type appelé "** Tensor type **". Comme le type ndarray, il peut effectuer des calculs matriciels et est assez similaire les uns aux autres, mais le type Tensor est supérieur en apprentissage automatique en ce sens qu'il peut utiliser le GPU. En effet, l'apprentissage automatique nécessite une quantité considérable de calculs et utilise un GPU avec une vitesse de calcul élevée. De plus, le type Tensor est très facile à différencier pour les mises à jour des paramètres d'apprentissage automatique. La clé de cet article est la facilité avec laquelle il est possible de le faire.
Veuillez vous référer au lien ci-dessous pour les opérations et les explications de type Tensor.
Qu'est-ce que le type de pyTorch Tensor
Tout d'abord, importez pour pouvoir utiliser pyTorch. À partir de là, écrivez dans le fichier python au lieu de cmd, etc. Utilisez le module en écrivant le code suivant.
filename.rb
import torch
Le programme de calcul simple suivant s'affiche.
filename.rb
x = torch.tensor(4.0, requires_grad = True)
c = torch.tensor(8.0)
b = 5.0
y = c*x + b
print(y)
------------Sortie ci-dessous---------------
tensor(37., grad_fn=<AddBackward0>)
C'est une formule
y = 8x+5
Il s'agit d'un calcul lorsque $ x = 4 $ de, et $ y $ est affiché comme 37. "** grad_fn = \ <AddBackward0 > **" de cette sortie est calculé en ajoutant $ y $ Cela montre qu'il a été calculé et qu'il est possible de le différencier en le maintenant dans chaque variable.
Cette différenciation est la suivante.
filename.rb
y.backward()
Cela différencie les valeurs de toutes les variables dans $ y $.
Rien n'est sorti, donc si vous le cochez
filename.rb
print(x)
print(x.grad)
------------Sortie ci-dessous---------------
tensor(4., requires_grad=True)
tensor(8.)
De cette façon, la sortie de $ x $ ne donne pas d'informations différentielles, mais vous pouvez voir la valeur différentielle 8.0 du nom de la variable en définissant "** x.grad **".
Ici, vous avez dit que vous avez différencié les valeurs de toutes les variables plus tôt, mais lorsque vous regardez réellement les informations différentielles des autres variables
filename.rb
print(c.grad)
print(b.grad)
------------Sortie ci-dessous---------------
None
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-881d89d572bd> in <module>
1 print(c.grad)
----> 2 print(b.grad)
AttributeError: 'float' object has no attribute 'grad'
Le premier résultat est "** Aucun ". En fait, " requires_grad = True **" n'a pas été ajouté à la variable ** c ** lorsque la première variable a été préparée. Cela amène la variable ** c ** à essayer de se différencier mais est interprétée comme une simple constante.
En outre, la deuxième sortie a une déclaration d'erreur. Il s'agit d'une erreur provoquée en essayant d'effectuer un calcul différentiel qui ne peut être effectué que sur le type Tensor, qui est un type spécial de pyTorch, vers un type autre que le type Tensor (cette variable ** b ** est juste un type float).
Cela montre que le type Tensor de pyTorch est très bon, et si vous définissez "requires_grad = True", toutes les informations différentielles seront calculées en une seule ligne.
Voici un exemple de calculs plus complexes.
filename.rb
x = torch.ones(2,3, requires_grad = True)
c = torch.ones(2,3, requires_grad = True)
y = torch.exp(x)*(c*3) + torch.exp(x)
print(torch.exp(x))
print(c*3)
print(y)
------------Sortie ci-dessous---------------
tensor([[2.7183, 2.7183, 2.7183],
[2.7183, 2.7183, 2.7183]], grad_fn=<ExpBackward>)
tensor([[3., 3., 3.],
[3., 3., 3.]], grad_fn=<MulBackward0>)
tensor([[10.8731, 10.8731, 10.8731],
[10.8731, 10.8731, 10.8731]], grad_fn=<AddBackward0>)
Tout d'abord, "** torch.exp () **" effectue un calcul qui devient $ e ^ {élément} $ pour chaque élément des données d'argument. Chaque sortie est comme vous pouvez le voir, cette fois, nous avons appliqué «requires_grad = True» aux deux variables ** x ** et ** c **.
Maintenant, quand en fait en arrière, cela devient comme suit.
filename.rb
y.backward()
------------Sortie ci-dessous---------------
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-11-ab75bb780f4c> in <module>
----> 1 y.backward()
......(réduction)......
RuntimeError: grad can be implicitly created only for scalar outputs
Une erreur est sortie. Comme indiqué dans cette erreur, le retour en arrière ne peut en fait être effectué que pour les valeurs scalaires (en termes simples, des données avec une seule valeur qui n'est pas une matrice ou un vecteur).
La solution réelle est la suivante.
filename.rb
s = torch.sum(y)
print(s)
------------Sortie ci-dessous---------------
tensor(65.2388, grad_fn=<SumBackward0>)
Ce "** torch.sum () **" renvoie le résultat de l'ajout de tous les éléments de l'argument. Vous avez maintenant une valeur scalaire. Quand tu fais vraiment en arrière
filename.rb
s.backward()
print(x.grad)
print(c.grad)
------------Sortie ci-dessous---------------
tensor([[10.8731, 10.8731, 10.8731],
[10.8731, 10.8731, 10.8731]])
tensor([[8.1548, 8.1548, 8.1548],
[8.1548, 8.1548, 8.1548]])
De cette manière, la différenciation est fermement effectuée même pour une matrice à plusieurs variables.
À partir de là, j'écrirai un exemple qui n'est pas réellement rétrogradé. À partir de là, j'ajouterai de nouveaux exemples dès que je les trouverai ou que je recevrai des rapports.
Comme expliqué dans l'exemple 5-2 de différenciation automatique ci-dessus, cela se produit lorsque la variable à différencier est ** Tensor type ** et "** requires_grad = True **". La solution est simple et le type doit répondre aux exigences.
Comme expliqué dans l'exemple 5-3 de différenciation automatique ci-dessus, cela se produit lorsque la variable que vous souhaitez différencier n'est ** pas une valeur scalaire **. La solution est d'en faire une valeur scalaire en quelque sorte. Par exemple, la somme des éléments effectuée dans l'exemple ci-dessus peut être effectuée sans casser la forme de la matrice.
Un exemple est présenté ci-dessous.
filename.rb
x = torch.tensor(1.0, requires_grad = True)
x = torch.exp(x)
c = torch.tensor(1.0, requires_grad = True)
c = c*3
b = 5.0
y = c*x + b
print(y)
------------Sortie ci-dessous---------------
tensor(13.1548, grad_fn=<AddBackward0>)
Si vous l'écrivez dans une formule avec un exemple très simple
y = (c*3)*e^{x}+5
La formule est $ c = 1 $, $ x = 1 $, et "requires_grad = True" rend c et x différents l'un de l'autre. La valeur différentielle réelle est la suivante.
filename.rb
y.backward()
print(x.grad)
print(c.grad)
------------Sortie ci-dessous---------------
None
None
Comment, ni x ni c n'ont une valeur différentielle. C'est parce que l'écrasement des variables élimine le processus de calcul de x et c (appelé le graphe de calcul) (la première définition de x et c, et tous les calculs passés qui ne sont pas effectués ici le sont ". Il sera écrasé par "torch.exp ()" et "* 3"). Cependant, dans un tel exemple, si vous essayez de le mettre dans l'optimiseur (SGD etc.) préparé par torch, vous obtiendrez une erreur. Un exemple réel est présenté ci-dessous.
filename.rb
import torch.optim as optim
op = optim.SGD([x,c], lr=1.0)
------------Sortie ci-dessous---------------
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-18-775027da6d38> in <module>
----> 1 op = optim.SGD([x,c], lr=1.0)
............(réduction)............
ValueError: can't optimize a non-leaf Tensor
Une erreur est générée lors de l'utilisation de SGD qui est un optimiseur. Si vous êtes intéressé par l'explication détaillée de cet optimiseur, veuillez vous référer au lien ci-dessous.
Explication approfondie de l'optimiseur PyTorch SGD
Comme je l'expliquerai brièvement ici, cette classe SGD se prépare à mettre à jour chaque paramètre du paramètre d'argument "** [x, c] **" en utilisant les informations de gradient. Cependant, à ce stade, cela donne une erreur que le graphe de calcul de ces variables est coupé.
La solution est de l'affecter à une autre variable sans écraser, ou d'écrire l'expression directement. Comme vous pouvez voir qu'elle est affectée à une autre variable, un exemple d'écriture directe de l'expression est présenté ci-dessous.
filename.rb
x = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(1.0, requires_grad = True)
b = 5.0
y = c*3*torch.exp(x)
y = y + b
y.backward()
print(x.grad)
print(c.grad)
------------Sortie ci-dessous---------------
tensor(8.1548)
tensor(8.1548)
Ici, les opérations de ** y ** sont intentionnellement séparées sur les 4e et 5e lignes. En fait, il n'y a aucune pénalité pour écraser ce ** y **. Puisque ** y ** n'est pas la variable que vous voulez différencier, peu importe si le calcul est fait correctement.
Tout d'abord, considérons le calcul suivant.
y = c\sqrt{x_1^2+x_2^2+x_3^2}
Comme vous pouvez le voir en un coup d'œil, il s'agit de la ** norme L2 ** (ou simplement de la distance) du vecteur $ [x_1, x_2, x_3] $ multipliée par c.
Ceci est montré par programme ci-dessous.
filename.rb
x = torch.tensor([2.0,5.0,3.0], requires_grad = True)
c = torch.tensor(2.0)
y = torch.sqrt(torch.sum(x**2))
y = y*c
y.backward()
print(x.grad)
------------Sortie ci-dessous---------------
tensor([0.6489, 1.6222, 0.9733])
On voit que la valeur différentielle de chaque élément lié au vecteur ** x ** peut être calculée correctement. Pour expliquer un peu le programme, "** torch.sqrt (torch.sum (x \ * \ * 2)) **" sur la troisième ligne place d'abord chaque élément de x et additionne chaque élément. , Et mettez-le à la racine.
Considérons maintenant l'exemple suivant avec cette équation.
filename.rb
x = torch.tensor([0.0,0.0,0.0], requires_grad = True)
c = torch.tensor(2.0)
y = torch.sqrt(torch.sum(x**2))
y = y*c
y.backward()
print(x.grad)
------------Sortie ci-dessous---------------
tensor([nan, nan, nan])
Maintenant, j'ai réécrit toutes les valeurs de chaque élément de la variable ** x ** à 0.0 (la distance du vecteur x est 0). En conséquence, toutes les valeurs différentielles sont devenues ** nan **. En faisant cela, la valeur différentielle de chaque élément prend naturellement ∞. Parce que la différenciation de l'équation ci-dessus est
\frac{\partial y}{\partial x_1} = c\frac{x_1}{\sqrt{x_1^2+x_2^2+x_3^2}}
Et comme la distance de $ x est de 0 $, elle sera divisée par 0. Dans le machine learning réel, les paramètres sont mis à jour automatiquement, mais si la valeur du paramètre devient 0 même une fois dans le processus, s'il y a $ \ sqrt {x} $ dans le processus de calcul, cela Le phénomène se produira. Cela fait diverger la perte ou devenir invisible.
La solution est la suivante.
filename.rb
x = torch.tensor([0.0,0.0,0.0], requires_grad = True)
c = torch.tensor(2.0)
y = torch.norm(x)
y = y*c
y.backward()
print(x.grad)
------------Sortie ci-dessous---------------
tensor([0., 0., 0.])
De cette façon, si vous utilisez "** torch.norm () **" sur la troisième ligne, la valeur différentielle ne sera pas ** nan ** mais sera 0. Ce torch.norm () fait exactement la même chose dans le calcul lui-même, mais il a probablement un mécanisme qui empêche la division 0 en interne.
python a une opération sur place qui vous permet d'effectuer les opérations suivantes:
filename.rb
i += 1
x *=3
Celles-ci sont généralement décrites en omettant les endroits où «** i = i + 1 » et « x = x * 3 **» sont écrits. Cette notation semble être plus rapide, mais elle n'est pas adaptée à la différenciation automatique. Un exemple est présenté ci-dessous.
filename.rb
x = torch.tensor(3.0, requires_grad = True)
c = torch.tensor(2.0)
c += 2.0
x += 2.0
y = x + c
------------Sortie ci-dessous---------------
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-94-beb1a427373d> in <module>
2 c = torch.tensor(2.0)
3 c += 2.0
----> 4 x += 2.0
5 y = x + c
RuntimeError: a leaf Variable that requires grad has been used in an in-place operation.
L'opération sur place n'est pas possible pour les variables avec "requires_grad = True" de cette manière (bien sûr, la variable c n'est pas liée à la différenciation).
La solution est simple et vous n'avez pas besoin d'utiliser une opération sur place. Autrement dit, tout ce que vous avez à faire est de l'écrire normalement.
Un exemple est présenté ci-dessous.
filename.rb
x = torch.tensor(3.0, requires_grad = True).cuda()
c = torch.tensor(2.0, requires_grad = True).cpu()
y = x*c
print(y)
------------Sortie ci-dessous---------------
tensor(6., device='cuda:0', grad_fn=<MulBackward0>)
Ici, la variable ** x ** est spécifiée pour utiliser gpu par "**. Cuda () ", et la variable ** c ** est spécifiée pour utiliser cpu par ". Cpu () ". faites. En outre, les deux variables sont dans un état divisible. La sortie de la réponse utilise gpu comme " device = 'cuda: 0' **".
Faisons en arrière
filename.rb
y.backward()
------------Sortie ci-dessous---------------
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-118-8117d53c0658> in <module>
----> 1 y.backward()
...........(réduction).............
RuntimeError: Function MulBackward0 returned an invalid gradient at index 1 - expected type torch.FloatTensor but got torch.cuda.FloatTensor
Cette erreur se produit car les variables à différencier de cette manière utilisent des ressources différentes.
La solution est de combiner les ressources utilisées les unes par les autres. Bien entendu, il n'est pas nécessaire de préparer des variables qui ne sont pas liées à la différenciation.
Parmi les types Tensor fournis par pyTorch, il existe également le type int, le type float, le type double, etc. Vous pouvez utiliser ce type correctement comme suit.
filename.rb
a = torch.tensor(2)
b = torch.tensor(2.134)
c = torch.tensor(3.5)
c = c.type(torch.int32)
d = torch.tensor(3.1514, dtype = torch.float64)
print(a)
print(b)
print(c)
print(d)
------------Sortie ci-dessous---------------
tensor(2)
tensor(2.1340)
tensor(3, dtype=torch.int32)
tensor(3.1514, dtype=torch.float64)
De cette façon, vous pouvez ajouter "** dtype = " au moment de la déclaration, ou ajouter " xxxx.type (type type) **". De plus, le type de chaque variable est vu comme suit.
filename.rb
print(a.dtype)
print(b.dtype)
print(c.dtype)
print(d.dtype)
------------Sortie ci-dessous---------------
torch.int64
torch.float32
torch.int32
torch.float64
Comme vous pouvez le voir, si vous passez un entier sans le spécifier au moment de la déclaration comme la variable a, il deviendra automatiquement ** int64 **, et si vous passez un nombre réel comme la variable b, il deviendra automatiquement ** float32 **. Ce qui est encore plus intéressant, c'est que pour la variable c, la conversion en int32 fait disparaître la partie fractionnaire.
Maintenant, sur la base de ce qui précède, le processus de calcul réel est présenté ci-dessous à titre d'exemple.
filename.rb
x = torch.tensor(3.0, dtype = torch.int64, requires_grad = True)
c = torch.tensor(2.0, requires_grad = True)
y = x*c
------------Sortie ci-dessous---------------
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-22-7183168e453f> in <module>
----> 1 x = torch.tensor(3.0, dtype = torch.int64, requires_grad = True)
2 c = torch.tensor(2.0, requires_grad = True)
3 y = x*c
RuntimeError: Only Tensors of floating point dtype can require gradients
Ici, j'ai essayé de faire de la variable ** x ** un type entier. S'il s'agit d'un type entier, il semble que "** requires_grad = True **" ne peut pas être défini en premier lieu, et cette erreur apparaît.
Réécrivons-le comme float.
filename.rb
x = torch.tensor(3.0, dtype = torch.float64, requires_grad = True)
c = torch.tensor(2.0, requires_grad = True)
y = x*c
print(y)
------------Sortie ci-dessous---------------
tensor(6., dtype=torch.float64, grad_fn=<MulBackward0>)
Cela a bien fonctionné. Faisons une différenciation automatique.
filename.rb
y.backward()
------------Sortie ci-dessous---------------
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-8-ab75bb780f4c> in <module>
----> 1 y.backward()
............(réduction)............
RuntimeError: Function MulBackward0 returned an invalid gradient at index 1 - expected type torch.FloatTensor but got torch.DoubleTensor
J'obtiens une erreur. La raison en est simple, en fait ** backward () ne peut être fait qu'avec le type torch.float32 **. Strictement parlant, ** torch.float64 ** préparé cette fois est traité comme du ** type double **, donc backward () ne peut pas être fait.
La solution consiste simplement à utiliser ** torch.float32 ** au lieu de ** torch.float64 **.
Dans le machine learning réel, il est courant de préparer et d'utiliser des bacteurs et des matrices comme paramètres. Un exemple est présenté ci-dessous.
filename.rb
x = torch.tensor([10.0,20.0,30.0], requires_grad = True)
c = torch.tensor([1.0,2.0,3.0], requires_grad = True)
x[0] = c[0]*x[0]
x[1] = c[1]*x[1]
x[2] = c[2]*x[2]
y = torch.sum(x)
print(y)
------------Sortie ci-dessous---------------
tensor(140., grad_fn=<SumBackward0>)
Il s'agit d'un programme qui calcule le produit interne du vecteur ** x ** et du vecteur ** c **. Maintenant, essayez en arrière ().
filename.rb
y.backward()
------------Sortie ci-dessous---------------
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-11-ab75bb780f4c> in <module>
----> 1 y.backward()
.........(réduction)..........
RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.FloatTensor []], which is output 0 of SelectBackward, is at version 3; expected version 2 instead. Hint: enable anomaly detection to find the operation that failed to compute its gradient, with torch.autograd.set_detect_anomaly(True).
Une erreur comme celle-ci a été générée. L'important ici est que l'erreur indique "** le calcul du gradient a été modifié par une opération inplace **", et j'ai donné un exemple d'in-place plus tôt, mais je ne le trouve nulle part dans ce programme. ..
En fait, ce calcul du tableau "** x [0] = c [0] * x [0] **" est équivalent à in-place. Si vous le regardez comme ceci, cela ressemble à une erreur comme l'écrasement de la variable mentionnée ci-dessus, mais soyez prudent car cela indique que l'erreur est causée par in-place. La solution consiste à utiliser le programme suivant.
filename.rb
x = torch.tensor([10.0,20.0,30.0], requires_grad = True)
c = torch.tensor([1.0,2.0,3.0], requires_grad = True)
w = torch.zeros(3)
w[0] = c[0]*x[0]
w[1] = c[1]*x[1]
w[2] = c[2]*x[2]
y = torch.sum(w)
print(y)
------------Sortie ci-dessous---------------
tensor(140., grad_fn=<SumBackward0>)
De cette manière, vous pouvez préparer des variables qui n'ont rien à voir avec la différenciation. Quand j'essaye en fait en arrière ()
filename.rb
y.backward()
print(x.grad)
------------Sortie ci-dessous---------------
tensor([1., 2., 3.])
Cela fonctionne bien.
Cette fois, j'ai résumé la partie automatique qui est invisible à l'arrière de pyTorch et l'exemple qui ne peut pas être fait. Cet article continuera d'être mis à jour dès que nous trouverons de tels exemples. Je pense qu'il y avait de nombreux points difficiles à lire, mais merci d'avoir lu.
Recommended Posts