Pytorch, các đối số gradient là gì


112

Tôi đang đọc tài liệu của PyTorch và tìm thấy một ví dụ nơi họ viết

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

trong đó x là một biến ban đầu, từ đó y được xây dựng (một vectơ 3). Câu hỏi là, các đối số 0,1, 1,0 và 0,0001 của tensor gradient là gì? Tài liệu không rõ ràng lắm về điều đó.

Câu trả lời:


15

Tôi không tìm thấy mã gốc trên trang web PyTorch nữa.

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

Vấn đề với đoạn mã trên không có chức năng nào dựa trên những gì để tính toán độ dốc. Điều này có nghĩa là chúng ta không biết có bao nhiêu tham số (đối số mà hàm nhận) và thứ nguyên của tham số.

Để hiểu đầy đủ điều này, tôi đã tạo một ví dụ gần với bản gốc:

Ví dụ 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])

Tôi giả sử hàm của chúng ta là y=3*a + 2*b*b + torch.log(c)và các tham số là tensors với ba phần tử bên trong.

Bạn có thể nghĩ gradients = torch.FloatTensor([0.1, 1.0, 0.0001])như thế này là bộ tích lũy.

Như bạn có thể nghe thấy hệ thống tính toán tự động của PyTorch tương đương với sản phẩm Jacobian.

Jacobian

Trong trường hợp bạn có một chức năng, như chúng tôi đã làm:

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

Jacobian sẽ là [3, 4*b, 1/c]. Tuy nhiên, Jacobian này không phải là cách PyTorch đang thực hiện để tính toán độ dốc tại một số điểm nhất định.

PyTorch sử dụng chế độ phân biệt tự động chuyển tiếp và lùi (AD) song song với nhau.

Không có toán học biểu tượng liên quan và không có phân biệt số.

Sự khác biệt về số sẽ là để tính toán δy/δb, cho b=1b=1+εở đâu ε là nhỏ.

Nếu bạn không sử dụng gradient trong y.backward():

Ví dụ 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.)

Bạn sẽ đơn giản nhận được kết quả tại một điểm, dựa vào cách bạn thiết của bạn a, b, ctensors ban đầu.

Hãy cẩn thận như thế nào bạn khởi tạo của bạn a, b, c:

Ví dụ 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])

Nếu bạn sử dụng torch.empty()và không sử dụng, pin_memory=Truebạn có thể có kết quả khác nhau mỗi lần.

Ngoài ra, lưu ý gradient giống như bộ tích lũy, do đó, không chúng khi cần thiết.

Ví dụ 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.)

Một số mẹo cuối cùng về các thuật ngữ PyTorch sử dụng:

PyTorch tạo một đồ thị tính toán động khi tính toán độ dốc trong chuyển tiếp. Nó trông giống như một cái cây.

Vì vậy, bạn sẽ thường nghe nói của cây này là tensor đầu vàogốctensor đầu ra .

Gradients được tính bằng cách truy tìm biểu đồ từ gốc đến lá và nhân mọi gradient theo cách sử dụng quy tắc chuỗi . Phép nhân này xảy ra trong quá trình chuyền ngược.


Câu trả lời chính xác! Tuy nhiên, tôi không nghĩ Pytorch thực hiện phân biệt số ("Đối với hàm trước đây, PyTorch sẽ làm ví dụ δy / δb, đối với b = 1 và b = 1 + ε trong đó ε là nhỏ. Vì vậy, không có gì giống như toán biểu tượng liên quan. ") - Tôi tin rằng nó thực hiện phân biệt tự động.
max_max_mir

Có, nó sử dụng AD, hoặc phân biệt tự động, sau này tôi đã tìm hiểu thêm về AD giống như trong PDF này , tuy nhiên, khi tôi đặt câu trả lời này, tôi đã không được thông báo.
prosti

Ví dụ: ví dụ 2 cho RuntimeError: Mismatch in shape: grad_output [0] có hình dạng của ngọn đuốc.Size ([3]) và đầu ra [0] có hình dạng của ngọn đuốc.Size ([]).
Andreas K.

@AndreasK., Bạn đã đúng, gần đây PyTorch đã giới thiệu các máy căng có kích thước bằng không và điều này đã ảnh hưởng đến các ví dụ trước đây của tôi. Đã xóa vì những ví dụ này không quan trọng.
prosti

100

Giải trình

Đối với mạng nơ-ron, chúng tôi thường sử dụng lossđể đánh giá mức độ học hỏi của mạng để phân loại hình ảnh đầu vào (hoặc các tác vụ khác). Các lossthuật ngữ thường là một giá trị vô hướng. Để cập nhật các tham số của mạng, chúng ta cần tính toán gradient của losswrt đối với các tham số, thực tế là leaf nodetrong đồ thị tính toán (nhân tiện, các tham số này chủ yếu là trọng số và độ lệch của các lớp khác nhau như Convolution, Linear và Sớm).

Theo quy tắc chuỗi, để tính toán gradient của losswrt cho một nút lá, chúng ta có thể tính đạo hàm của losswrt một biến trung gian nào đó và gradient của biến trung gian wrt với biến lá, thực hiện một tích chấm và tổng tất cả chúng lại.

Các gradientđối số của phương thức a Variable's backward()được sử dụng để tính tổng có trọng số của từng phần tử của một Biến với biến lá . Các trọng số này chỉ là dẫn xuất của losswrt cuối cùng mỗi phần tử của biến trung gian.

Một ví dụ cụ thể

Hãy lấy một ví dụ cụ thể và đơn giản để hiểu điều này.

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)    

Trong ví dụ trên, kết quả đầu tiên print

2 0 0 0
[torch.FloatTensor có kích thước 1x4]

chính xác là đạo hàm của z_1 wrt thành x.

Kết quả thứ hai printlà:

0 2 0 0
[torch.FloatTensor có kích thước 1x4]

là đạo hàm của z_2 wrt thành x.

Bây giờ nếu sử dụng trọng số [1, 1, 1, 1] để tính đạo hàm của z wrt thành x, kết quả là 1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx. Vì vậy, không có gì đáng ngạc nhiên, đầu ra của thứ 3 printlà:

2 2 2 2
[torch.FloatTensor có kích thước 1x4]

Cần lưu ý rằng vectơ trọng số [1, 1, 1, 1] là đạo hàm chính xác của losswrt thành z_1, z_2, z_3 và z_4. Đạo hàm của losswrt tới xđược tính như sau:

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

Vì vậy, đầu ra của thứ 4 printgiống với thứ 3 print:

2 2 2 2
[torch.FloatTensor có kích thước 1x4]


1
chỉ là một nghi ngờ, tại sao chúng ta tính x.grad.data cho gradient cho mất mát hoặc z.
Priyank Pathak

7
Có lẽ tôi đã bỏ lỡ điều gì đó, nhưng tôi cảm thấy như tài liệu chính thức thực sự có thể giải thích gradientlập luận tốt hơn. Cảm ơn câu trả lời của bạn.
nhân vật chính

3
@jdhao "Cần lưu ý rằng vector trọng lượng [1, 1, 1, 1]chính xác là dẫn xuất của losswrt để z_1, z_2, z_3z_4." Tôi nghĩ câu nói này thực sự là chìa khóa cho câu trả lời. Khi nhìn vào mã của OP, một dấu hỏi lớn là những con số (ma thuật) tùy ý này cho gradient đến từ đâu. Trong ví dụ cụ thể của bạn, tôi nghĩ sẽ rất hữu ích nếu chỉ ra mối quan hệ giữa ví dụ [1, 0, 0 0]tensor và losshàm ngay lập tức để người ta có thể thấy rằng các giá trị không phải là tùy ý trong ví dụ này.
a_guest

1
@smwikipedia, điều đó không đúng. Nếu chúng ta mở rộng loss = z.sum(dim=1), nó sẽ trở thành loss = z_1 + z_2 + z_3 + z_4. Nếu bạn biết phép tính đơn giản, bạn sẽ biết rằng đạo hàm của losswrt để z_1, z_2, z_3, z_4[1, 1, 1, 1].
jdhao

1
Tôi mến bạn. Đã giải quyết nghi ngờ của tôi!
Black Jack 21

45

Thông thường, đồ thị tính toán của bạn có một đầu ra vô hướng cho biết loss. Sau đó, bạn có thể tính toán gradient của losswrt the weights ( w) theo loss.backward(). Trường hợp đối số mặc định của backward()1.0.

Nếu đầu ra của bạn có nhiều giá trị (ví dụ loss=[loss1, loss2, loss3]), bạn có thể tính toán các mức độ mất mát theo trọng số loss.backward(torch.FloatTensor([1.0, 1.0, 1.0])).

Hơn nữa, nếu bạn muốn thêm trọng số hoặc nhập khẩu cho các khoản lỗ khác nhau, bạn có thể sử dụng loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001])).

Điều này có nghĩa là phải tính toán -0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dwđồng thời.


1
"nếu bạn muốn thêm trọng số hoặc nhập khẩu vào các khoản lỗ khác nhau, bạn có thể sử dụng loss.backward (torch.FloatTensor ([- 0,1, 1,0, 0,0001]))." -> Điều này đúng nhưng hơi sai lầm vì lý do chính khiến chúng ta vượt qua grad_tensorskhông phải để cân chúng khác nhau mà chúng là gradient wrt từng phần tử của tenxơ tương ứng.
Aerin

27

Ở đây, đầu ra của forward (), tức là y là aa 3 vector.

Ba giá trị là độ dốc ở đầu ra của mạng. Chúng thường được đặt thành 1,0 nếu y là đầu ra cuối cùng, nhưng cũng có thể có các giá trị khác, đặc biệt nếu y là một phần của mạng lớn hơn.

Ví dụ. nếu x là đầu vào, y = [y1, y2, y3] là đầu ra trung gian được sử dụng để tính đầu ra cuối cùng z,

Sau đó,

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

Vì vậy, ở đây, ba giá trị lùi lại là

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

và sau đó back () tính dz / dx


5
Cảm ơn vì câu trả lời nhưng điều này hữu ích trong thực tế như thế nào? Ý tôi là chúng ta cần [dz / dy1, dz / dy2, dz / dy3] ngoài backprop mã hóa cứng ở đâu?
hi15

Có chính xác không khi nói rằng đối số gradient được cung cấp là gradient được tính trong phần sau của mạng?
Khanetor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.