Ví dụ từng bước về phân biệt tự động chế độ đảo ngược


27

Không chắc câu hỏi này có thuộc về vấn đề này không, nhưng nó liên quan chặt chẽ đến các phương pháp gradient trong tối ưu hóa, dường như là chủ đề ở đây. Dù sao, hãy thoải mái di chuyển nếu bạn nghĩ rằng một số cộng đồng khác có chuyên môn tốt hơn trong chủ đề này.

Nói tóm lại, tôi đang tìm một ví dụ từng bước về phân biệt tự động chế độ đảo ngược . Không có nhiều tài liệu về chủ đề ngoài kia và việc triển khai hiện có (như bài viết trong TensorFlow ) thật khó hiểu nếu không biết lý thuyết đằng sau nó. Vì vậy, tôi rất biết ơn nếu ai đó có thể trình bày chi tiết những gì chúng tôi vượt qua , cách chúng tôi xử lý nó và những gì chúng tôi đưa ra khỏi biểu đồ tính toán.

Một vài câu hỏi mà tôi gặp khó khăn nhất:

  • hạt giống - tại sao chúng ta cần chúng ở tất cả?
  • quy tắc phân biệt ngược - Tôi biết làm thế nào để tạo sự khác biệt về phía trước, nhưng làm thế nào để chúng ta đi lùi? Ví dụ như trong ví dụ từ phần này , làm thế nào để chúng ta biết rằng w2¯=w3¯w1 ?
  • chúng ta chỉ làm việc với các biểu tượng hoặc chuyển qua các giá trị thực tế ? Ví dụ, trong cùng một ví dụ , các ký hiệu hoặc giá trị wiwi¯ ?

"Học máy thực hành với Scikit-Learn & TensorFlow" Phụ lục D đưa ra một lời giải thích rất tốt theo quan điểm của tôi. Tôi khuyến khích điều đó.
Agustin Barrachina

Câu trả lời:


37

Giả sử chúng ta có biểu thức z=x1x2+sin(x1) và muốn tìm đạo hàm dzdx1dzdx2 . AD chế độ đảo ngược chia nhiệm vụ này thành 2 phần, cụ thể là chuyển tiếp và đảo ngược.

Chuyển tiếp qua

Đầu tiên, chúng ta phân tách biểu thức phức tạp của chúng ta thành một tập hợp các biểu thức nguyên thủy, tức là các biểu thức bao gồm nhiều nhất là một hàm gọi. Lưu ý rằng tôi cũng đổi tên các biến đầu vào và đầu ra để thống nhất, mặc dù không cần thiết:

w1=x1
w2=x2
w3=w1w2
w4=sin(w1)
w5=w3+w4
z=w5

Ưu điểm của biểu diễn này là các quy tắc phân biệt cho từng biểu thức riêng biệt đã được biết đến. Ví dụ, chúng ta biết rằng đạo hàm của sincos , và do đó dw4dw1=cos(w1) . Chúng tôi sẽ sử dụng thực tế này trong vượt qua dưới đây.

Về cơ bản, chuyển tiếp bao gồm đánh giá từng biểu thức này và lưu kết quả. Giả sử, đầu vào của chúng tôi là: x1=2x2=3 . Sau đó chúng tôi có:

w1=x1=2
w2=x2=3
w3=w1w2=6
w4=sin(w1) =0.9
w5=w3+w4=6.9
z=w5=6.9

Đảo ngược

Đây là sự khởi đầu kỳ diệu, và nó bắt đầu với quy tắc chuỗi . Ở dạng cơ bản, quy tắc chuỗi nói rằng nếu bạn có biến t(u(v)) phụ thuộc vào u , đến lượt nó, phụ thuộc vào v , thì:

dtdv=dtdududv

hoặc, nếu t phụ thuộc vào v thông qua một số đường dẫn / biến ui , ví dụ:

u1=f(v)
u2=g(v)
t=h(u1,u2)

sau đó (xem bằng chứng ở đây ):

dtdv=idtduiduidv

Về mặt biểu đồ, nếu chúng ta có một nút cuối cùng z và các nút đầu vào wi và đường dẫn từ z đến wi đi qua các nút trung gian wp (tức là z=g(wp) trong đó wp=f(wi) ), chúng ta có thể tìm đạo hàm dzdwi như

dzdwi=pparents(i)dzdwpdwpdwi

Nói cách khác, để tính đạo hàm của biến đầu ra z wrt bất kỳ biến trung gian hoặc biến đầu vào wi , chúng ta chỉ cần biết đạo hàm của cha mẹ của nó và công thức để tính đạo hàm của biểu thức nguyên thủy wp=f(wi) .

Đảo ngược bắt đầu ở cuối (tức là dzdz) and propagates backward to all dependencies. Here we have (expression for "seed"):

dzdz=1

That may be read as "change in z results in exactly the same change in z", which is quite obvious.

Then we know that z=w5 and so:

dzdw5=1

w5 linearly depends on w3 and w4, so dw5dw3=1dw5dw4=1 . Sử dụng quy tắc chuỗi chúng tôi tìm thấy:

dzdw3=dzdw5dw5dw3=1×1=1
dzdw4=dzdw5dw5dw4=1×1=1

From definition w3=w1w2 and rules of partial derivatives, we find that dw3dw2=w1. Thus:

dzdw2=dzdw3dw3dw2=1×w1=w1

Which, as we already know from forward pass, is:

dzdw2=w1=2

Finally, w1 contributes to z via w3 and w4. Once again, from the rules of partial derivatives we know that dw3dw1=w2 and dw4dw1=cos(w1). Thus:

dzdw1=dzdw3dw3dw1+dzdw4dw4dw1=w2+cos(w1)

And again, given known inputs, we can calculate it:

dzdw1=w2+cos(w1)=3+cos(2) =2.58

Since w1 and w2 are just aliases for x1 and x2, we get our answer:

dzdx1=2.58
dzdx2=2

And that's it!


This description concerns only scalar inputs, i.e. numbers, but in fact it can also be applied to multidimensional arrays such as vectors and matrices. Two things that one should keep in mind when differentiating expressions with such objects:

  1. Derivatives may have much higher dimensionality than inputs or output, e.g. derivative of vector w.r.t. vector is a matrix and derivative of matrix w.r.t. matrix is a 4-dimensional array (sometimes referred to as a tensor). In many cases such derivatives are very sparse.
  2. Each component in output array is an independent function of 1 or more components of input array(s). E.g. if y=f(x) and both x and y are vectors, yi never depends on yj, but only on subset of xk. In particular, this means that finding derivative dyidxj boils down to tracking how yi depends on xj.

The power of automatic differentiation is that it can deal with complicated structures from programming languages like conditions and loops. However, if all you need is algebraic expressions and you have good enough framework to work with symbolic representations, it's possible to construct fully symbolic expressions. In fact, in this example we could produce expression dzdw1=w2+cos(w1)=x2+cos(x1) and calculate this derivative for whatever inputs we want.


1
Very useful question/answer. Thanks. Just a litte criticism: you seem to move on a tree structure without explaining (that's when you start talking about parents, etc..)
MadHatter

1
Also it won't hurt clarifying why we need seeds.
MadHatter

@MadHatter thanks for the comment. I tried to rephrase a couple of paragraphs (these that refer to parents) to emphasize a graph structure. I also added "seed" to the text, although this name itself may be misleading in my opinion: in AD seed is always a fixed expression - dzdz=1, not something you can choose or generate.
ffriend

Thanks! I noticed when you have to set more than one "seed", generally one chooses 1 and 0. I'd like to know why. I mean, one takes the "quotient" of a differential w.r.t. itself, so "1" is at least intuitively justified.. But what about 0? And what if one has to pick more than 2 seeds?
MadHatter

1
As far as I understand, more than one seed is used only in forward-mode AD. In this case you set the seed to 1 for an input variable you want to differentiate with respect to and set the seed to 0 for all the other input variables so that they don't contribute to the output value. In reverse-mode you set the seed to an output variable, and you normally have only one output variable. I guess, you can construct reverse-mode AD pipeline with several output variables and set all of them but one to 0 to get the same effect as in forward mode, but I have never investigated this option.
ffriend
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.