当前位置:网站首页>Jouer avec la classe de fonctions de pytorch
Jouer avec la classe de fonctions de pytorch
2022-06-10 17:46:00 【Wu Lele】
Catalogue des articles
Préface
pytorchOffreautogradMécanisme de dérivation automatique,EtautogradLa réalisation de la dérivation automatique passe essentiellement parFunctionClasse implémentée.Et les partenaires qui ont l'habitude de construire des blocs n'écrivent pasbackward.Créer une situation où vous avez besoin d'étendre l'opérateur et vous serez submergé.Cet article commence par des exemples simples,Apprendre à réaliser unFunctionLes éléments les plus fondamentaux de la classe,Il y a aussi quelques considérations,Enfin, je combine un vrai combat pour apprendreFunctionUtilisation de la classe.
1、y=w*x+b
import torch
from torch.autograd import Function
# y = w*x + b Une propagation vers l'avant et une dérivation inverse de
class Mul(Function):
@staticmethod
def forward(ctx, w, x, b, x_requires_grad = True): # ctx Peut être compris comme l'ancêtre. , Variable de cache intermédiaire utilisée pour stocker les gradients .
ctx.save_for_backward(w,b) # Parce quedy/dx = w; dy/dw = x ; dy/db = 1; Les variables intermédiaires doivent être sauvegardées pour une Rétropropagation ultérieure w,x
output = w*x + b
return output
@staticmethod
def backward(ctx,grad_outputs): # Ici.grad_outputs Analyse spécifique des problèmes spécifiques
w = ctx.saved_tensors[0] # Enlevez - le.ctxSauvegardé dans w = 2
b = ctx.saved_tensors[1] # Enlevez - le.ctxSauvegardé dans b = 3
grad_w = grad_outputs * x # 1 * 1 = 1
grad_x = grad_outputs * w # 1 * 2 = 2
grad_b = grad_outputs * 1 # 1 * 1 = 1
return grad_w, grad_x, grad_b, None # Paramètres retournés et forwardLes paramètres correspondent un par un,Pour les paramètresx_requires_grad Retour direct sans gradient None.
if __name__ == '__main__':
x = torch.tensor(1.,requires_grad=True)
w = torch.tensor(2.,requires_grad=True)
b = torch.tensor(3., requires_grad=True)
y = Mul.apply(w,x,b) # y = w*x + b = 2*1 + 3 = 5
print('forward:', y)
# Écrire un
loss = y.sum() # Convertir en scalaire
loss.backward() # Rétropropagation:Parce que loss = sum(y),Donc...grad_outputs = dloss/dy = 1,Peut être omis
print(' Gradient d'écriture 1 :',x.grad, w.grad, b.grad) # tensor(2.) tensor(1.) tensor(1.)
Pour résumer: Commentaires dans le Code .Parmi euxy=w*x+b. La propagation vers l'avant est facile à comprendre . Ce qui est déroutant ici, c'est que ctxCe truc., En fait, c'est un ancêtre. ,Par la méthodesave_for_backward() Pour enregistrer les variables de cache intermédiaires propagées vers l'avant , Conditions de Rétropropagation ultérieure . Et en Rétropropagation, ,D'abord à partir dectx Méthode d'appel saved_tensors[]Viens.w,b. Gradient des paramètres suivants :dy/dx = w; dy/dw = x; dy/db = 1.
En plus,En Rétropropagation, La confusion est le paramètre grad_outputs. En fait, la valeur de ce paramètre est liée à la fin de l'appel de classe .Dans le Code,Utiliserloss.backward(), Vous pouvez voir que le paramètre entrant est vide . C'est parce que la propagation vers l'avant yAprès,loss = y.sum(),C'est - à - dire:grad_outputs = dloss/dy = 1; EttorchMoyenne,Peut être omis. Donc ici grad_outputs=1.
Bien sûr., On peut aussi y participer explicitement. .
# Écrire un
loss1 = y.sum() # Convertir en scalaire
loss1.backward() # Rétropropagation:Parce que loss = sum(y),Donc...grad_outputs = dloss/dy = 1,Peut être omis
print(' Gradient d'écriture 1 :',x.grad, w.grad, b.grad) # tensor(2.) tensor(1.) tensor(1.)
# écriture II
loss2 = y.sum()
loss2.backward(torch.tensor(1.))
print(' Gradient d'écriture II :',x.grad, w.grad, b.grad) # tensor(4.) tensor(2.) tensor(2.)
Mais c'est une erreur. ,Les informations d'erreur sont les suivantes::
RuntimeError: Trying to backward through the graph a second time, but the saved intermediate results have already been freed. Specify retain_graph=True when calling backward the first time.
En gros, le même graphique ne peut pas être inversé deux fois. .Parce que, La première fois que vous appelez backwardAprès, Les calculs sont détruits. . Donc vous devez définir les paramètres retain_graph Diagramme de calcul de l'enregistrement des paramètres ,Le Code modifié est le suivant::
# Écrire un
loss1 = y.sum() # Convertir en scalaire
loss1.backward(retain_graph = True) # Rétropropagation:Parce que loss = sum(y),Donc...grad_outputs = dloss/dy = 1,Peut être omis
print(' Gradient d'écriture 1 :',x.grad, w.grad, b.grad) # tensor(2.) tensor(1.) tensor(1.)
# écriture II
loss2 = y.sum()
loss2.backward(torch.tensor(1.))
print(' Gradient d'écriture II :',x.grad, w.grad, b.grad) # tensor(4.) tensor(2.) tensor(2.)
Malheureusement,, À ce stade, les résultats du calcul du gradient entre la méthode d'écriture 2 et la méthode d'écriture 1 ne sont pas cohérents. , On trouve que le gradient d'écriture 2 est deux fois plus élevé que celui d'écriture 1. .C'est parce quepytorch Deux fois différentes loss Au noeud foliaire lors de l'inversion du gradient Les gradients sont cumulatifs.Donc,, Nous devons éliminer le gradient de perte 1 entre la propagation de la perte 2 0.Les codes sont les suivants::
# Écrire un
loss1 = y.sum() # Convertir en scalaire
loss1.backward(retain_graph = True) # Rétropropagation:Parce que loss = sum(y),Donc...grad_outputs = dloss/dy = 1,Peut être omis
print(' Gradient d'écriture 1 :',x.grad, w.grad, b.grad) # tensor(2.) tensor(1.) tensor(1.)
# Dégagement du gradient du noeud foliaire 0
x.grad.zero_()
w.grad.zero_()
b.grad.zero_()
# écriture II
loss2 = y.sum()
loss2.backward(torch.tensor(1.))
print(' Gradient d'écriture II :',x.grad, w.grad, b.grad) # tensor(2.) tensor(1.) tensor(1.)
OK,Un grand succès.Le code complet est le suivant:
import torch
from torch.autograd import Function
# y = w*x + b Une propagation vers l'avant et une dérivation inverse de
class Mul(Function):
@staticmethod
def forward(ctx, w, x, b, x_requires_grad = True): # ctx Peut être compris comme l'ancêtre. , Variable de cache intermédiaire utilisée pour stocker les gradients .
ctx.save_for_backward(w,b) # Parce quedy/dx = w; dy/dw = x ; dy/db = 1; Les variables intermédiaires doivent être sauvegardées pour une Rétropropagation ultérieure w,x
output = w*x + b
return output
@staticmethod
def backward(ctx,grad_outputs): # Ici.grad_outputs Analyse spécifique des problèmes spécifiques
w = ctx.saved_tensors[0] # Enlevez - le.ctxSauvegardé dans w = 2
b = ctx.saved_tensors[1] # Enlevez - le.ctxSauvegardé dans b = 3
grad_w = grad_outputs * x # 1 * 1 = 1
grad_x = grad_outputs * w # 1 * 2 = 2
grad_b = grad_outputs * 1 # 1 * 1 = 1
return grad_w, grad_x, grad_b, None # Paramètres retournés et forwardLes paramètres correspondent un par un,Pour les paramètresx_requires_grad Retour direct sans gradient None.
if __name__ == '__main__':
x = torch.tensor(1.,requires_grad=True)
w = torch.tensor(2.,requires_grad=True)
b = torch.tensor(3., requires_grad=True)
y = Mul.apply(w,x,b) # y = w*x + b = 2*1 + 3 = 5
print('forward:', y)
# Écrire un
loss1 = y.sum() # Convertir en scalaire
loss1.backward(retain_graph = True) # Rétropropagation:Parce que loss = sum(y),Donc...grad_outputs = dloss/dy = 1,Peut être omis
print(' Gradient d'écriture 1 :',x.grad, w.grad, b.grad) # tensor(2.) tensor(1.) tensor(1.)
# Dégagement du gradient du noeud foliaire 0
x.grad.zero_()
w.grad.zero_()
b.grad.zero_()
# écriture II
loss2 = y.sum()
loss2.backward(torch.tensor(1.))
print(' Gradient d'écriture II :',x.grad, w.grad, b.grad) # tensor(4.) tensor(2.) tensor(2.)
2、Niveau avancé:y=exp(x)*2
import torch
from torch.autograd import Function
class Exp(Function):
@staticmethod
def forward(ctx,x):
output = x.exp()
ctx.save_for_backward(output) # dy/dx = exp(x)
return output
@staticmethod
def backward(ctx, grad_outputs): # dloss/dx = grad_outputs* exp(x)
output = ctx.saved_tensors[0]
return output*grad_outputs
if __name__ == '__main__':
x = torch.tensor(1.,requires_grad=True)
y = Exp.apply(x)
print(y)
y = y * 2
loss = y.sum()
loss.backward()
print(x.grad)
La seule chose à noter est :dloss/dy = 1 * 2 = 2;Parce queloss = sum(2y).
3、Sur le terrain:GuideReLUFonctions
ReLUFonctions:y=max(x,0), Uniquement lorsque le gradient est inversé x>0 C'est là qu'il y a un gradient. , Et la valeur du gradient est 1.Parce quey=x,Alors...dy/dx=1;EtGuideReLUOui.ReLUBase,Non seulementx>0 La position peut inverser le gradient , Le Gradient doit également être satisfait >0 La position peut inverser le gradient .dloss/dx = dloss/dy * (x>0) * (grad_output>0).Les codes sont les suivants::
import torch
from torch.autograd import Function
class GuideReLU(Function):
@staticmethod
def forward(ctx,input):
output = torch.clamp(input,min=0)
ctx.save_for_backward(output)
return output
@staticmethod
def backward(ctx, grad_outputs): # dloss/dx = dloss/dy * !(x > 0) * (dloss/dy > 0)
output = ctx.saved_tensors[0] # dloss/dy
return grad_outputs * (output>0).float()* (grad_outputs>0).float()
if __name__ == '__main__':
x = torch.randn(2,3,requires_grad=True)
print('input:',x)
y = GuideReLU.apply(x)
print('forward:',y)
grad_y = torch.randn(2,3)
y.backward(grad_y) # Une valeur de gradient est reçue ici ,C'est - à - dire:grad_outputs
print('grad_y:',grad_y) # C'est - à - dire seulement si vous entrez x Et le gradient de retour grad_yEn même temps>0 La position a une valeur de gradient .
print('grad_x:',x.grad)
4、Contrôle du gradient:torch.autograd.gradcheck()
pytorch Fournit un contrôle de gradient api, Il est facile de vérifier la propagation correcte de votre écriture .
import torch
from torch.autograd import Function
class Sigmoid(Function):
@staticmethod
def forward(ctx, x):
output = 1 / (1 + torch.exp(-x))
ctx.save_for_backward(output)
return output
@staticmethod
def backward(ctx, grad_output):
output, = ctx.saved_tensors
grad_x = output * (1 - output) * grad_output
return grad_x
test_input = torch.randn(4, requires_grad=True) # tensor([-0.4646, -0.4403, 1.2525, -0.5953], requires_grad=True)
print(torch.autograd.gradcheck(Sigmoid.apply, (test_input,), eps=1e-3))
Résumé
Ce chapitre est une introduction pytorch Le premier chapitre de la conduction inverse , Présentation et développement des réunions de suivi C++/CUDAOpérateur. Si vous avez des questions, bienvenue. +vx:wulele2541612007, Vous amener dans un groupe pour discuter de la communication .
边栏推荐
猜你喜欢

Leetcode 929. 独特的电子邮件地址

Nacos registry

Online communication skill network: a sparse model for solving multi task and multi-modal problems (Qingyuan talk, issue 19, tangduyu)

基于PHP+Web+Mysql的在线问卷调查系统

Gateway service gateway

厉害了,工信部推出 “一键解绑” 手机号绑定的互联网账号,堪称神器

嘿!ONES 新星请看过来|师兄师姐说

PCA主成分分析教程(origin分析&绘制,无须R语言)

自定义视图:图形与图像的处理(一):使用简单图片

2022年焊工(初级)试题模拟考试平台操作
随机推荐
Internet enterprises and chips
KDD 2021 | MoCl: comparative learning of molecular graphs using multi-level domain knowledge
Lifeifei: I am more like a scientist in physics than an engineer
Nat. Commun. | Knowledge integration and decision support for accelerating the discovery of antibiotic resistance genes
See how advanced technology changes human life
2022年T电梯修理考试题模拟考试题库及在线模拟考试
Detailed derivation of perspective projection transformation and related applications
vscode常用插件与配置
信息学奥赛一本通 1280:【例9.24】滑雪 | OpenJudge NOI 2.6 90:滑雪 | 洛谷 P1434 [SHOI2002] 滑雪
厉害了,工信部推出 “一键解绑” 手机号绑定的互联网账号,堪称神器
力扣 20. 有效的括号
软件项目管理 6.10.成本预算
Mapbox GL development tutorial (11): loading line layers
元宇宙的定义和 7 大无限特征
PCA主成分分析教程(origin分析&绘制,无须R语言)
Fabric. JSON for JS compact output
如何运行plink软件--三种方法
树、森林和二叉树的关系
flex布局语法
Station B doesn't want to be a "conscience aiyouteng"