Pytorch, gradyan argümanları nelerdir


112

PyTorch'un belgelerini okuyorum ve yazdıkları bir örnek buldum

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)

burada x, y'nin oluşturulduğu bir başlangıç ​​değişkeni (3-vektör). Soru, gradyan tensörünün 0.1, 1.0 ve 0.0001 argümanları nelerdir? Belgeler bu konuda çok net değil.

Yanıtlar:


15

Artık PyTorch web sitesinde bulamadığım orijinal kod.

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)

Yukarıdaki kodla ilgili problem, degradelerin hesaplanmasına dayalı bir işlev yoktur. Bu, kaç parametre (fonksiyonun aldığı argümanlar) ve parametrelerin boyutunu bilmediğimiz anlamına gelir.

Bunu tam olarak anlamak için orijinaline yakın bir örnek oluşturdum:

Örnek 1:

a = torch.tensor([1.0, 2.0, 3.0], requires_grad = True)
b = torch.tensor([3.0, 4.0, 5.0], requires_grad = True)
c = torch.tensor([6.0, 7.0, 8.0], requires_grad = True)

y=3*a + 2*b*b + torch.log(c)    
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients,retain_graph=True)    

print(a.grad) # tensor([3.0000e-01, 3.0000e+00, 3.0000e-04])
print(b.grad) # tensor([1.2000e+00, 1.6000e+01, 2.0000e-03])
print(c.grad) # tensor([1.6667e-02, 1.4286e-01, 1.2500e-05])

Fonksiyonumuzun y=3*a + 2*b*b + torch.log(c)ve parametrelerin içinde üç element bulunan tensörler olduğunu varsaydım .

Bunun gradients = torch.FloatTensor([0.1, 1.0, 0.0001])akümülatör olduğunu düşünebilirsiniz .

PyTorch otomatik derecelendirme sistemi hesaplamasının Jacobian ürününe eşdeğer olduğunu duymuşsunuzdur.

Jacobi

Yaptığımız gibi bir işleviniz olması durumunda:

y=3*a + 2*b*b + torch.log(c)

Jacobian olurdu [3, 4*b, 1/c]. Bununla birlikte, bu Jacobian , PyTorch'un belirli bir noktada gradyanları hesaplamak için yaptığı şeyler değildir.

PyTorch, ardışık olarak ileri geçiş ve geri mod otomatik farklılaşmasını (AD) kullanır .

Sembolik matematik söz konusu değildir ve sayısal farklılaşma yoktur.

Sayısal türev hesaplamak olacaktır δy/δbiçin, b=1ve b=1+εε küçük olduğu.

Aşağıdakilerde degradeler kullanmıyorsanız y.backward():

Örnek 2

a = torch.tensor(0.1, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(0.1, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)

y.backward()

print(a.grad) # tensor(3.)
print(b.grad) # tensor(4.)
print(c.grad) # tensor(10.)

Basit Eğer set şekline göre, bir noktada sonuç elde edecek a, b, cbaşlangıçta tensörleri.

Eğer senin başlatmak kadar dikkatli olun a, b, c:

Örnek 3:

a = torch.empty(1, requires_grad = True, pin_memory=True)
b = torch.empty(1, requires_grad = True, pin_memory=True)
c = torch.empty(1, requires_grad = True, pin_memory=True)

y=3*a + 2*b*b + torch.log(c)

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)

print(a.grad) # tensor([3.3003])
print(b.grad) # tensor([0.])
print(c.grad) # tensor([inf])

Kullanır torch.empty()ve kullanmazsanız pin_memory=True, her seferinde farklı sonuçlar elde edebilirsiniz.

Ayrıca, gradyanlar akümülatörler gibidir, bu nedenle gerektiğinde onları sıfırlayın.

Örnek 4:

a = torch.tensor(1.0, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(1.0, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)

y.backward(retain_graph=True)
y.backward()

print(a.grad) # tensor(6.)
print(b.grad) # tensor(8.)
print(c.grad) # tensor(2.)

Son olarak, PyTorch'un kullandığı terimlerle ilgili birkaç ipucu:

PyTorch, ileri geçişte degradeleri hesaplarken dinamik bir hesaplama grafiği oluşturur . Bu bir ağaca çok benziyor.

Bu nedenle , bu ağacın yapraklarının girdi tensörleri ve kökünün çıkış tensörü olduğunu sık sık duyacaksınız .

Gradyanlar, grafiğin kökten yaprağa izlenmesiyle ve zincir kuralını kullanarak her gradyanın çarpılmasıyla hesaplanır . Bu çarpma, geriye doğru geçişte meydana gelir.


Mükemmel cevap! Bununla birlikte, Pytorch'un sayısal farklılaştırma yaptığını sanmıyorum ("Önceki fonksiyon için PyTorch, örneğin δy / δb, b = 1 ve b = 1 + ε için ε'nin küçük olduğu yerlerde yapacaktır. Yani sembolik matematik gibi bir şey yoktur. ") - Otomatik farklılaşma yaptığına inanıyorum.
max_max_mir

Evet, AD veya otomatik farklılaştırma kullanıyor, daha sonra AD'yi bu PDF'deki gibi daha fazla araştırdım , ancak bu cevabı verdiğimde tam olarak bilgilendirilmemiştim.
prosti

Örnek 2, RuntimeError: Mismatch'i biçim olarak verir: grad_output [0], torch.Size ([3]) şeklinde ve çıktı [0], torch.Size ([]) şeklindedir.
Andreas K.

@AndreasK., Haklıydın, PyTorch yakın zamanda sıfır boyutlu tensörleri tanıttı ve bu benim önceki örneklerimi etkiledi. Bu örnekler çok önemli olmadığı için kaldırıldı.
prosti

100

açıklama

Sinir ağları için, genellikle lossağın giriş görüntüsünü (veya diğer görevleri) sınıflandırmayı ne kadar iyi öğrendiğini değerlendirmek için kullanırız . lossTerim genellikle skaler bir değerdir. Ağın parametrelerini güncellemek için loss, gerçekte leaf nodehesaplama grafiğinde bulunan parametrelere göre wrt'nin gradyanını hesaplamamız gerekir (bu arada, bu parametreler çoğunlukla Konvolüsyon, Doğrusal gibi çeşitli katmanların ağırlığı ve önyargısıdır. yakında).

Zincir kuralına göre, losswrt'nin bir yaprak düğüme gradyanını hesaplamak için, losswrt'nin bir ara değişkeninin türevini ve wrt'nin yaprak değişkenine olan gradyanını hesaplayabilir, bir iç çarpım yapabilir ve hepsini toplayabiliriz .

gradientBir argümanları Variablebireyin backward()yöntem için kullanılan wrt Değişken her elemanı bir ağırlıklı toplamını hesaplamak yaprak Değişken . Bu ağırlık, lossara değişkenin her bir elemanının nihai sonucunun sadece türevidir .

Somut bir örnek

Bunu anlamak için somut ve basit bir örnek alalım.

from torch.autograd import Variable
import torch
x = Variable(torch.FloatTensor([[1, 2, 3, 4]]), requires_grad=True)
z = 2*x
loss = z.sum(dim=1)

# do backward for first element of z
z.backward(torch.FloatTensor([[1, 0, 0, 0]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_() #remove gradient in x.grad, or it will be accumulated

# do backward for second element of z
z.backward(torch.FloatTensor([[0, 1, 0, 0]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_()

# do backward for all elements of z, with weight equal to the derivative of
# loss w.r.t z_1, z_2, z_3 and z_4
z.backward(torch.FloatTensor([[1, 1, 1, 1]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_()

# or we can directly backprop using loss
loss.backward() # equivalent to loss.backward(torch.FloatTensor([1.0]))
print(x.grad.data)    

Yukarıdaki örnekte, birincinin sonucu printşudur:

2 0 0 0
[1x4 boyutlu torç FloatTensor]

bu tam olarak z_1 wrt'nin x'e türevidir.

Saniyenin sonucu print :

0 2 0 0
[1x4 boyutunda torç FloatTensor]

ki bu z_2 wrt'nin x'e türevidir.

Şimdi, z wrt'nin x'e türevini hesaplamak için [1, 1, 1, 1] ağırlığını kullanırsanız, sonuç şu olur 1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx. Şaşırtıcı olmayan bir şekilde, 3. çıktıprint :

2 2 2 2
[1x4 boyutunda torç FloatTensor]

Ağırlık vektörünün [1, 1, 1, 1] tam lossolarak wrt'nin z_1, z_2, z_3 ve z_4'e türevi olduğuna dikkat edilmelidir. lossWrt'nin türevi xşu şekilde hesaplanır:

d(loss)/dx = d(loss)/dz_1 * dz_1/dx + d(loss)/dz_2 * dz_2/dx + d(loss)/dz_3 * dz_3/dx + d(loss)/dz_4 * dz_4/dx

Dolayısıyla 4. çıktısı print3. ile aynıdır print:

2 2 2 2
[1x4 boyutunda torç FloatTensor]


1
sadece bir şüphe, neden kayıp veya z için gradyanlar için x.grad.data'yı hesaplıyoruz ?
Priyank Pathak

7
Belki bir şeyi gözden kaçırmışımdır, ancak resmi belgelerin gradientargümanı gerçekten daha iyi açıklayabileceğini düşünüyorum . Cevabınız için teşekkürler.
kahramanı

3
@jdhao "ağırlık vektörü unutulmamalıdır [1, 1, 1, 1]tam türevi lossiçin wrt z_1, z_2, z_3ve z_4." Bence bu ifade, cevabın gerçekten anahtarı. OP'nin koduna bakıldığında , gradyan için bu rastgele (sihirli) sayıların nereden geldiği büyük bir soru işaretidir . Somut örneğinizde, örneğin [1, 0, 0 0]tensör ve lossfonksiyon arasındaki ilişkiye hemen işaret etmenin çok yararlı olacağını düşünüyorum , böylece bu örnekte değerlerin keyfi olmadığını görebiliriz.
a_guest

1
@smwikipedia, bu doğru değil. Genişlersek loss = z.sum(dim=1), olur loss = z_1 + z_2 + z_3 + z_4. Eğer basit bir hesaplama yöntemi biliyorsanız, türevi bilecek losswrt için z_1, z_2, z_3, z_4olduğunu [1, 1, 1, 1].
jdhao

1
Seni seviyorum. Şüphemi çözdüm!
Black Jack 21

45

Tipik olarak, hesaplama grafiğinizin bir skaler çıktısı vardır: loss . Daha sonra lossağırlıkların ( w) gradyanı ile hesaplayabilirsiniz loss.backward(). Nerede varsayılan argüman backward()DİR 1.0.

Çıktınızın birden fazla değeri varsa (örneğin loss=[loss1, loss2, loss3]), ağırlıklara göre kayıp gradyanlarını hesaplayabilirsiniz loss.backward(torch.FloatTensor([1.0, 1.0, 1.0])).

Ayrıca, farklı kayıplara ağırlık veya ithalat eklemek isterseniz, loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001])) .

Bu -0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dwaynı anda hesaplamak anlamına gelir .


1
"Farklı kayıplara ağırlık veya önem eklemek istiyorsanız, loss.backward (torch.FloatTensor ([- 0.1, 1.0, 0.0001])) kullanabilirsiniz." -> Bu doğru ama biraz yanıltıcı çünkü geçmemizin ana nedeni grad_tensorsonları farklı şekilde tartmak değil, karşılık gelen tensörlerin her bir elemanı için gradyanlar.
Aerin

27

Burada forward () çıktısı, yani y aa 3-vektördür.

Üç değer, ağın çıktısındaki gradyanlardır. Eğer y nihai çıktıysa bunlar genellikle 1.0'a ayarlanır, ancak başka değerlere de sahip olabilirler, özellikle y daha büyük bir ağın parçasıysa.

Örneğin. x girdi ise, y = [y1, y2, y3], son çıktı z'yi hesaplamak için kullanılan bir ara çıktıdır,

Sonra,

dz/dx = dz/dy1 * dy1/dx + dz/dy2 * dy2/dx + dz/dy3 * dy3/dx

Yani burada geriye doğru üç değer

[dz/dy1, dz/dy2, dz/dy3]

ve sonra geriye doğru () dz / dx'i hesaplar


5
Cevabınız için teşekkürler, ancak bu pratikte nasıl yararlıdır? Demek istediğim, [dz / dy1, dz / dy2, dz / dy3] 'e, geriye dönük kodlama dışında nereye ihtiyacımız var?
hi15

Sağlanan gradyan argümanının ağın ikinci kısmında hesaplanan gradyan olduğunu söylemek doğru mu?
Khanetor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.