Pytorch, apa saja argumen gradien

112

Saya membaca dokumentasi PyTorch dan menemukan contoh di mana mereka menulis

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

di mana x adalah variabel awal, dari mana y dibangun (vektor 3). Pertanyaannya adalah, apa argumen 0.1, 1.0 dan 0.0001 dari tensor gradien? Dokumentasinya tidak terlalu jelas tentang itu.

Qubix
sumber

Jawaban:

15

Kode asli yang tidak saya temukan di situs web PyTorch lagi.

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

Masalah dengan kode di atas tidak ada fungsi berdasarkan apa yang menghitung gradien. Ini berarti kita tidak tahu berapa banyak parameter (argumen yang dibutuhkan fungsi) dan dimensi parameter.

Untuk sepenuhnya memahami ini, saya membuat contoh yang mirip dengan aslinya:

Contoh 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])

Saya berasumsi fungsi kami adalah y=3*a + 2*b*b + torch.log(c)dan parameternya adalah tensor dengan tiga elemen di dalamnya.

Anda bisa menganggap yang gradients = torch.FloatTensor([0.1, 1.0, 0.0001])seperti ini adalah akumulator.

Seperti yang mungkin Anda dengar, perhitungan sistem autograd PyTorch setara dengan produk Jacobian.

Jacobian

Jika Anda memiliki fungsi, seperti yang kami lakukan:

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

Jacobian akan menjadi [3, 4*b, 1/c]. Namun, Jacobian ini bukanlah cara PyTorch melakukan sesuatu untuk menghitung gradien pada titik tertentu.

PyTorch menggunakan penerusan maju dan diferensiasi otomatis mode mundur (AD) secara bersamaan.

Tidak ada matematika simbolik yang terlibat dan tidak ada diferensiasi numerik.

Diferensiasi numerik adalah menghitung δy/δb, untuk b=1dan di b=1+εmana ε kecil.

Jika Anda tidak menggunakan gradien di y.backward():

Contoh 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.)

Anda akan mudah mendapatkan hasil pada suatu titik, didasarkan pada bagaimana Anda mengatur Anda a, b, ctensor awalnya.

Hati-hati bagaimana Anda menginisialisasi Anda a, b, c:

Contoh 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])

Jika Anda menggunakan torch.empty()dan tidak menggunakan pin_memory=TrueAnda mungkin memiliki hasil yang berbeda setiap saat.

Juga, gradien catatan seperti akumulator jadi nolkan mereka saat diperlukan.

Contoh 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.)

Beberapa tip terakhir tentang istilah yang digunakan PyTorch:

PyTorch membuat grafik komputasi dinamis saat menghitung gradien dalam lintasan maju. Ini terlihat seperti pohon.

Jadi anda akan sering mendengar daun pohon ini adalah input tensor dan root adalah output tensor .

Gradien dihitung dengan menelusuri grafik dari akar ke daun dan mengalikan setiap gradien dengan menggunakan aturan rantai . Pengalikan ini terjadi pada gerakan mundur.

prosti
sumber
Jawaban yang bagus! Namun, menurut saya Pytorch tidak melakukan diferensiasi numerik ("Untuk fungsi sebelumnya PyTorch akan melakukan misalnya δy / δb, untuk b = 1 dan b = 1 + ε di mana ε kecil. Jadi, tidak ada matematika simbolik yang terlibat. ") - Saya yakin ini melakukan diferensiasi otomatis.
max_max_mir
Ya, ini menggunakan AD, atau diferensiasi otomatis, kemudian saya selidiki AD lebih lanjut seperti di PDF ini , namun, ketika saya menetapkan jawaban ini saya kurang mendapat informasi.
prosti
Misal contoh 2 memberikan RuntimeError: Mismatch in shape: grad_output [0] berbentuk torch.Size ([3]) dan output [0] berbentuk torch.Size ([]).
Andreas K.
@AndreasK., Anda benar, PyTorch baru-baru ini memperkenalkan tensor berukuran nol dan ini berdampak pada contoh saya sebelumnya. Dihapus karena contoh ini tidak penting.
prosti
100

Penjelasan

Untuk jaringan saraf, kami biasanya menggunakan lossuntuk menilai seberapa baik jaringan telah belajar untuk mengklasifikasikan gambar masukan (atau tugas lain). The lossIstilah ini biasanya nilai skalar. Untuk memperbarui parameter jaringan, kita perlu menghitung gradien losswrt ke parameter, yang sebenarnya ada leaf nodedalam grafik komputasi (omong-omong, parameter ini sebagian besar merupakan bobot dan bias dari berbagai lapisan seperti Konvolusi, Linear dan seterusnya).

Menurut aturan rantai, untuk menghitung gradien losswrt ke simpul daun, kita dapat menghitung turunan dari losswrt beberapa variabel perantara, dan gradien variabel perantara wrt ke variabel daun, lakukan perkalian titik dan jumlahkan semuanya.

The gradientargumen dari Variable's backward()metode yang digunakan untuk menghitung jumlah tertimbang dari setiap elemen dari Variabel wrt Variable daun . Bobot ini hanyalah turunan dari losswrt akhir setiap elemen variabel menengah.

Contoh konkret

Mari kita ambil contoh konkret dan sederhana untuk memahami ini.

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)    

Dalam contoh di atas, hasil dari yang pertama printadalah

2 0 0 0
[obor.FloatTensor ukuran 1x4]

yang persis merupakan turunan dari z_1 wrt ke x.

Hasil kedua printadalah:

0 2 0 0
[torch.FloatTensor ukuran 1x4]

yang merupakan turunan dari z_2 wrt ke x.

Sekarang jika menggunakan bobot [1, 1, 1, 1] untuk menghitung turunan dari z wrt ke x, hasilnya adalah 1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx. Jadi tidak mengherankan jika keluaran dari ke-3 printadalah:

2 2 2 2
[torch.FloatTensor ukuran 1x4]

Perlu dicatat bahwa vektor bobot [1, 1, 1, 1] persis turunan dari losswrt ke z_1, z_2, z_3 dan z_4. Turunan dari losswrt to xdihitung sebagai:

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

Jadi output ke-4 printsama dengan yang ke-3 print:

2 2 2 2
[torch.FloatTensor ukuran 1x4]

jdhao
sumber
1
hanya keraguan, mengapa kita menghitung x.grad.data untuk gradien untuk kerugian atau z.
Priyank Pathak
7
Mungkin saya melewatkan sesuatu, tetapi saya merasa bahwa dokumentasi resmi benar-benar dapat menjelaskan gradientargumen dengan lebih baik. Terima kasih atas jawaban anda.
protagonis
3
@jdhao "Perlu dicatat bahwa vektor bobot [1, 1, 1, 1]persis turunan dari losswrt untuk z_1, z_2, z_3dan z_4." Saya pikir pernyataan ini benar-benar kunci jawabannya. Saat melihat kode OP, tanda tanya besar adalah dari mana asal angka - angka (ajaib) untuk gradien ini. Dalam contoh konkret Anda, saya pikir akan sangat membantu untuk menunjukkan hubungan antara [1, 0, 0 0]tensor eg dan lossfungsi segera sehingga orang dapat melihat bahwa nilainya tidak sembarangan dalam contoh ini.
Tamu
1
@smwikipedia, itu tidak benar. Jika kita berkembang loss = z.sum(dim=1), itu akan menjadi loss = z_1 + z_2 + z_3 + z_4. Jika Anda mengetahui kalkulus sederhana, Anda akan mengetahui bahwa turunan dari losswrt to z_1, z_2, z_3, z_4adalah [1, 1, 1, 1].
jdhao
1
Aku cinta kamu. Memecahkan keraguan saya!
Black Jack 21
45

Biasanya, grafik komputasi Anda memiliki satu keluaran skalar loss. Kemudian Anda dapat menghitung gradien dari losswrt the weights ( w) dengan loss.backward(). Di mana argumen default backward()adalah 1.0.

Jika keluaran Anda memiliki beberapa nilai (misalnya loss=[loss1, loss2, loss3]), Anda dapat menghitung gradien kerugian dengan bobot loss.backward(torch.FloatTensor([1.0, 1.0, 1.0])).

Selanjutnya, jika Anda ingin menambah bobot atau kepentingan kerugian yang berbeda, Anda dapat menggunakan loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001])).

Artinya menghitung -0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dwsecara bersamaan.

Gu Wang
sumber
1
"jika Anda ingin menambahkan bobot atau kepentingan pada kerugian yang berbeda, Anda dapat menggunakan loss.backward (torch.FloatTensor ([- 0.1, 1.0, 0.0001]))." -> Ini benar tetapi agak menyesatkan karena alasan utama mengapa kami lulus grad_tensorsbukanlah untuk menimbangnya secara berbeda tetapi mereka adalah gradien dari setiap elemen tensor yang sesuai.
Aerin
27

Di sini, keluaran dari forward (), yaitu y adalah aa 3-vektor.

Ketiga nilai tersebut adalah gradien pada keluaran jaringan. Mereka biasanya disetel ke 1.0 jika y adalah keluaran akhir, tetapi dapat memiliki nilai lain juga, terutama jika y adalah bagian dari jaringan yang lebih besar.

Misalnya. jika x adalah masukan, y = [y1, y2, y3] adalah keluaran antara yang digunakan untuk menghitung keluaran akhir z,

Kemudian,

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

Jadi di sini, tiga nilai mundur adalah

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

dan kemudian mundur () menghitung dz / dx

greenberet123
sumber
5
Terima kasih atas jawabannya tetapi bagaimana ini berguna dalam praktiknya? Maksud saya, di mana kita membutuhkan [dz / dy1, dz / dy2, dz / dy3] selain backprop hardcode?
hi15
Apakah benar untuk mengatakan bahwa argumen gradien yang diberikan adalah gradien yang dihitung di bagian akhir jaringan?
Khanetor