<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 2.3 自動求梯度 在深度學習中,我們經常需要對函數求梯度(gradient)。PyTorch提供的[autograd](https://pytorch.org/docs/stable/autograd.html)包能夠根據輸入和前向傳播過程自動構建計算圖,并執行反向傳播。本節將介紹如何使用autograd包來進行自動求梯度的有關操作。 ## 2.3.1 概念 上一節介紹的`Tensor`是這個包的核心類,如果將其屬性`.requires_grad`設置為`True`,它將開始追蹤(track)在其上的所有操作(這樣就可以利用鏈式法則進行梯度傳播了)。完成計算后,可以調用`.backward()`來完成所有梯度計算。此`Tensor`的梯度將累積到`.grad`屬性中。 > 注意在`y.backward()`時,如果`y`是標量,則不需要為`backward()`傳入任何參數;否則,需要傳入一個與`y`同形的`Tensor`。解釋見 2.3.2 節。 如果不想要被繼續追蹤,可以調用`.detach()`將其從追蹤記錄中分離出來,這樣就可以防止將來的計算被追蹤,這樣梯度就傳不過去了。此外,還可以用`with torch.no_grad()`將不想被追蹤的操作代碼塊包裹起來,這種方法在評估模型的時候很常用,因為在評估模型時,我們并不需要計算可訓練參數(`requires_grad=True`)的梯度。 `Function`是另外一個很重要的類。`Tensor`和`Function`互相結合就可以構建一個記錄有整個計算過程的有向無環圖(DAG)。每個`Tensor`都有一個`.grad_fn`屬性,該屬性即創建該`Tensor`的`Function`, 就是說該`Tensor`是不是通過某些運算得到的,若是,則`grad_fn`返回一個與這些運算相關的對象,否則是None。 下面通過一些例子來理解這些概念。 ## 2.3.2 `Tensor` 創建一個`Tensor`并設置`requires_grad=True`: ~~~ ?x = torch.ones(2, 2, requires_grad=True) ?print(x) ?print(x.grad_fn) ~~~ 輸出: ~~~ ?tensor([[1., 1.], ? ? ? ? [1., 1.]], requires_grad=True) ?None ~~~ 再做一下運算操作: ~~~ ?y = x + 2 ?print(y) ?print(y.grad_fn) ~~~ 輸出: ~~~ ?tensor([[3., 3.], ? ? ? ? [3., 3.]], grad_fn=<AddBackward>) ?<AddBackward object at 0x1100477b8> ~~~ 注意x是直接創建的,所以它沒有`grad_fn`, 而y是通過一個加法操作創建的,所以它有一個為`<AddBackward>`的`grad_fn`。 像x這種直接創建的稱為葉子節點,葉子節點對應的`grad_fn`是`None`。 ~~~ ?print(x.is_leaf, y.is_leaf) # True False ~~~ 再來點復雜度運算操作: ~~~ ?z = y * y * 3 ?out = z.mean() ?print(z, out) ~~~ 輸出: ~~~ ?tensor([[27., 27.], ? ? ? ? [27., 27.]], grad_fn=<MulBackward>) tensor(27., grad_fn=<MeanBackward1>) ~~~ 通過`.requires_grad_()`來用in-place的方式改變`requires_grad`屬性: ~~~ ?a = torch.randn(2, 2) # 缺失情況下默認 requires_grad = False ?a = ((a * 3) / (a - 1)) ?print(a.requires_grad) # False ?a.requires_grad_(True) ?print(a.requires_grad) # True ?b = (a * a).sum() ?print(b.grad_fn) ~~~ 輸出: ~~~ False True <SumBackward0 object at 0x118f50cc0> ~~~ ## 2.3.2 梯度 因為`out`是一個標量,所以調用`backward()`時不需要指定求導變量: ~~~ out.backward() # 等價于 out.backward(torch.tensor(1.)) ~~~ 我們來看看`out`關于`x`的梯度 \\frac{d(out)}{dx}: ~~~ print(x.grad) ~~~ 輸出: ~~~ tensor([[4.5000, 4.5000], [4.5000, 4.5000]]) ~~~ 我們令`out`為 o , 因為 所以 所以上面的輸出是正確的。 數學上,如果有一個函數值和自變量都為向量的函數 \\vec{y}=f(\\vec{x}), 那么 \\vec{y} 關于 \\vec{x} 的梯度就是一個雅可比矩陣(Jacobian matrix): 而`torch.autograd`這個包就是用來計算一些雅克比矩陣的乘積的。例如,如果 v 是一個標量函數的 l=g\\left(\\vec{y}\\right) 的梯度: 那么根據鏈式法則我們有 l 關于 \\vec{x} 的雅克比矩陣就為: 注意:grad在反向傳播過程中是累加的(accumulated),這意味著每一次運行反向傳播,梯度都會累加之前的梯度,所以一般在反向傳播之前需把梯度清零。 ~~~ # 再來反向傳播一次,注意grad是累加的 out2 = x.sum() out2.backward() print(x.grad) out3 = x.sum() x.grad.data.zero_() out3.backward() print(x.grad) ~~~ 輸出: ~~~ tensor([[5.5000, 5.5000], [5.5000, 5.5000]]) tensor([[1., 1.], [1., 1.]]) ~~~ > 現在我們解釋2.3.1節留下的問題,為什么在`y.backward()`時,如果`y`是標量,則不需要為`backward()`傳入任何參數;否則,需要傳入一個與`y`同形的`Tensor`? 簡單來說就是為了避免向量(甚至更高維張量)對張量求導,而轉換成標量對張量求導。舉個例子,假設形狀為 `m x n` 的矩陣 X 經過運算得到了 `p x q` 的矩陣 Y,Y 又經過運算得到了 `s x t` 的矩陣 Z。那么按照前面講的規則,dZ/dY 應該是一個 `s x t x p x q` 四維張量,dY/dX 是一個 `p x q x m x n`的四維張量。問題來了,怎樣反向傳播?怎樣將兩個四維張量相乘???這要怎么乘???就算能解決兩個四維張量怎么乘的問題,四維和三維的張量又怎么乘?導數的導數又怎么求,這一連串的問題,感覺要瘋掉…… 為了避免這個問題,我們**不允許張量對張量求導,只允許標量對張量求導,求導結果是和自變量同形的張量**。所以必要時我們要把張量通過將所有張量的元素加權求和的方式轉換為標量,舉個例子,假設`y`由自變量`x`計算而來,`w`是和`y`同形的張量,則`y.backward(w)`的含義是:先計算`l = torch.sum(y * w)`,則`l`是個標量,然后求`l`對自變量`x`的導數。 [參考](https://zhuanlan.zhihu.com/p/29923090) 來看一些實際例子。 ~~~ x = torch.tensor([1.0, 2.0, 3.0, 4.0], requires_grad=True) y = 2 * x z = y.view(2, 2) print(z) ~~~ 輸出: ~~~ tensor([[2., 4.], [6., 8.]], grad_fn=<ViewBackward>) ~~~ 現在 `y` 不是一個標量,所以在調用`backward`時需要傳入一個和`y`同形的權重向量進行加權求和得到一個標量。 ~~~ v = torch.tensor([[1.0, 0.1], [0.01, 0.001]], dtype=torch.float) z.backward(v) print(x.grad) ~~~ 輸出: ~~~ tensor([2.0000, 0.2000, 0.0200, 0.0020]) ~~~ 注意,`x.grad`是和`x`同形的張量。 再來看看中斷梯度追蹤的例子: ~~~ x = torch.tensor(1.0, requires_grad=True) y1 = x ** 2 with torch.no_grad(): y2 = x ** 3 y3 = y1 + y2 print(x.requires_grad) print(y1, y1.requires_grad) # True print(y2, y2.requires_grad) # False print(y3, y3.requires_grad) # True ~~~ 輸出: ~~~ True tensor(1., grad_fn=<PowBackward0>) True tensor(1.) False tensor(2., grad_fn=<ThAddBackward>) True ~~~ 可以看到,上面的`y2`是沒有`grad_fn`而且`y2.requires_grad=False`的,而`y3`是有`grad_fn`的。如果我們將`y3`對`x`求梯度的話會是多少呢? ~~~ y3.backward() print(x.grad) ~~~ 輸出: ~~~ tensor(2.) ~~~ 為什么是2呢? y\_3 = y\_1 + y\_2 = x^2 + x^3,當 x=1 時 \\frac {dy\_3} {dx} 不應該是5嗎?事實上,由于 y\_2 的定義是被`torch.no_grad():`包裹的,所以與 y\_2 有關的梯度是不會回傳的,只有與 y\_1 有關的梯度才會回傳,即 x^2 對 x 的梯度。 上面提到,`y2.requires_grad=False`,所以不能調用 `y2.backward()`,會報錯: ~~~ RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn ~~~ 此外,如果我們想要修改`tensor`的數值,但是又不希望被`autograd`記錄(即不會影響反向傳播),那么我么可以對`tensor.data`進行操作。 ~~~ x = torch.ones(1,requires_grad=True) print(x.data) # 還是一個tensor print(x.data.requires_grad) # 但是已經是獨立于計算圖之外 y = 2 * x x.data *= 100 # 只改變了值,不會記錄在計算圖,所以不會影響梯度傳播 y.backward() print(x) # 更改data的值也會影響tensor的值 print(x.grad) ~~~ 輸出: ~~~ tensor([1.]) False tensor([100.], requires_grad=True) tensor([2.]) ~~~ * * * > 注: 本文主要參考[PyTorch官方文檔](https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html#sphx-glr-beginner-blitz-autograd-tutorial-py),與[原書同一節](https://zh.d2l.ai/chapter_prerequisite/autograd.html)有很大不同。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看