95 votes

Méthode préférée de Pytorch pour copier un tenseur

Il semble y avoir plusieurs façons de créer une copie d'un tenseur dans Pytorch, y compris

y = tensor.new_tensor(x) #a

y = x.clone().detach() #b

y = torch.empty_like(x).copy_(x) #c

y = torch.tensor(x) #d

b est explicitement préféré à a y d d'après un UserWarning que j'obtiens si j'exécute soit a o d . Pourquoi est-il préféré ? La performance ? Je dirais que c'est moins lisible.

Les raisons pour ou contre l'utilisation de c ?

6 votes

Un avantage de b est qu'il rend explicite le fait que y ne fait plus partie du graphe de calcul, c'est-à-dire qu'il ne nécessite pas de gradient. c est différent des 3 en ce que y nécessite toujours un diplôme.

1 votes

Et si torch.empty_like(x).copy_(x).detach() - c'est la même chose que a/b/d ? Je reconnais que ce n'est pas une façon intelligente de procéder, j'essaie simplement de comprendre le fonctionnement de l'autograd. Je suis confus par le documents pour clone() qui disent "Contrairement à copy_(), cette fonction est enregistrée dans le graphe de calcul," ce qui m'a fait penser à copy_() ne nécessiterait pas de gradation.

3 votes

Il y a une note assez explicite dans les docs : When data is a tensor x, new_tensor() reads out ‘the data’ from whatever it is passed, and constructs a leaf variable. Therefore tensor.new_tensor(x) is equivalent to x.clone().detach() and tensor.new_tensor(x, requires_grad=True) is equivalent to x.clone().detach().requires_grad_(True). The equivalents using clone() and detach() are recommended.

79voto

kHarshit Points 856

TL;DR

Utilice .clone().detach() (ou de préférence .detach().clone() )

Si vous détachez d'abord le tenseur puis le clonez, le chemin de calcul n'est pas copié, dans l'autre sens, il est copié puis abandonné. Ainsi, .detach().clone() est très légèrement plus efficace forums pytorch

car il est légèrement rapide et explicite dans ce qu'il fait.


Utilisation de perflot j'ai tracé le timing de différentes méthodes pour copier un tenseur pytorch.

y = tensor.new_tensor(x) # method a

y = x.clone().detach() # method b

y = torch.empty_like(x).copy_(x) # method c

y = torch.tensor(x) # method d

y = x.detach().clone() # method e

L'axe des x est la dimension du tenseur créé, l'axe des y indique le temps. Le graphique est en échelle linéaire. Comme vous pouvez le voir clairement, le tensor() o new_tensor() prend plus de temps par rapport aux trois autres méthodes.

enter image description here

Nota: En plusieurs essais, j'ai remarqué que parmi b, c, e, n'importe quelle méthode peut avoir le temps le plus bas. Il en va de même pour a et d. Mais, les méthodes b, c, e ont systématiquement un temps plus faible que a et d.

import torch
import perfplot

perfplot.show(
    setup=lambda n: torch.randn(n),
    kernels=[
        lambda a: a.new_tensor(a),
        lambda a: a.clone().detach(),
        lambda a: torch.empty_like(a).copy_(a),
        lambda a: torch.tensor(a),
        lambda a: a.detach().clone(),
    ],
    labels=["new_tensor()", "clone().detach()", "empty_like().copy()", "tensor()", "detach().clone()"],
    n_range=[2 ** k for k in range(15)],
    xlabel="len(a)",
    logx=False,
    logy=False,
    title='Timing comparison for copying a pytorch tensor',
)

16voto

Nopileos Points 1188

Selon Documentation sur Pytorch #a et #b sont équivalents. On dit aussi que

Les équivalents utilisant clone() et detach() sont recommandés.

Donc, si vous voulez copier un tenseur et vous détacher du graphe de calcul, vous devriez utiliser

y = x.clone().detach()

Puisque c'est le moyen le plus propre et le plus lisible. Avec toutes les autres versions, il y a une certaine logique cachée et il n'est pas clair à 100% ce qui arrive au graphe de calcul et à la propagation du gradient.

Concernant le point C : Il semble un peu trop compliqué pour ce qui est réellement fait et pourrait également introduire une certaine surcharge mais je ne suis pas sûr de cela.

Edit : Puisque cela a été demandé dans les commentaires, pourquoi ne pas simplement utiliser .clone() .

De la docs sur pytorch

Contrairement à copy_(), cette fonction est enregistrée dans le graphe de calcul. Les gradients se propageant vers le tenseur cloné se propageront vers le tenseur original.

Alors que .clone() retourne une copie des données, il conserve le graphe de calcul et y enregistre l'opération de clonage. Comme mentionné, le gradient propagé au tenseur cloné se propage également au tenseur original. Ce comportement peut conduire à des erreurs et n'est pas évident. En raison de ces effets secondaires possibles, un tenseur ne devrait être cloné que par le biais de .clone() si ce comportement est explicitement souhaité. Pour éviter ces effets secondaires, l'option .detach() est ajouté pour déconnecter le graphe de calcul du tenseur cloné.

Étant donné qu'en général, pour une opération de copie, on veut une copie propre qui ne peut pas entraîner d'effets secondaires imprévus, la manière préférée de copier un tenseur est la suivante .clone().detach() .

3voto

Manoj Acharya Points 469

Pytorch '1.1.0' recommande #b maintenant et montre un avertissement pour #d

0 votes

Qu'en est-il .clone() par lui-même ?

0 votes

Clone par lui-même gardera également la variable attachée au graphique original

2voto

prosti Points 4630

Un exemple pour vérifier si le tenseur est copié :

import torch
def samestorage(x,y):
    if x.storage().data_ptr()==y.storage().data_ptr():
        print("same storage")
    else:
        print("different storage")
a = torch.ones((1,2), requires_grad=True)
print(a)
b = a
c = a.data
d = a.detach()
e = a.data.clone()
f = a.clone()
g = a.detach().clone()
i = torch.empty_like(a).copy_(a)
j = torch.tensor(a) # UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).

print("a:",end='');samestorage(a,a)
print("b:",end='');samestorage(a,b)
print("c:",end='');samestorage(a,c)
print("d:",end='');samestorage(a,d)
print("e:",end='');samestorage(a,e)
print("f:",end='');samestorage(a,f)
print("g:",end='');samestorage(a,g)
print("i:",end='');samestorage(a,i)

Dehors :

tensor([[1., 1.]], requires_grad=True)
a:same storage
b:same storage
c:same storage
d:same storage
e:different storage
f:different storage
g:different storage
i:different storage
j:different storage

Le tenseur est copié si le stockage différent se montre. PyTorch a presque 100 constructeurs différents, donc vous pouvez ajouter beaucoup plus de façons.

Si j'avais besoin de copier un tenseur, j'utiliserais simplement copy() cela copie également les informations liées à AD, donc si je devais supprimer les informations liées à AD, j'utiliserais :

y = x.clone().detach()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X