一.前言
本章節我們是要學習梯隊計算,?動微分(Autograd)模塊對張量做了進?步的封裝,具有?動求導功能。?動微分模塊是構成神經?絡 訓練的必要模塊,在神經?絡的反向傳播過程中,Autograd 模塊基于正向計算的結果對當前的參數進?微 分計算,從?實現?絡權重參數的更新。
二.梯度基本計算
我們使? backward ?法、grad 屬性來實現梯度的計算和訪問.
import torch# 1. 單標量梯度的計算
# y = x**2 + 20
def test01():# 定義需要求導的張量# 張量的值類型必須是浮點類型x = torch.tensor(10, requires_grad=True, dtype=torch.float64)# 變量經過中間運算f = x ** 2 + 20# ?動微分f.backward()# 打印 x 變量的梯度# backward 函數計算的梯度值會存儲在張量的 grad 變量中print(x.grad)# 2. 單向量梯度的計算# y = x**2 + 20def test02():# 定義需要求導張量x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)# 變量經過中間計算f1 = x ** 2 + 20# 注意:# 由于求導的結果必須是標量# ? f 的結果是: tensor([120., 420.])# 所以, 不能直接?動微分# 需要將結果計算為標量才能進?計算f2 = f1.mean() # f2 = 1/2 * x 2x/4# ?動微分f2.backward()# 打印 x 變量的梯度print(x.grad)if __name__ == '__main__':test01()test02()
tensor(20., dtype=torch.float64)
tensor([ 5., 10., 15., 20.], dtype=torch.float64)?
三.控制梯度計算?
我們可以通過?些?法使得在 requires_grad=True 的張量在某些時候計算不進?梯度計算。?
import torch# 1. 控制不計算梯度
def test01():x = torch.tensor(10, requires_grad=True, dtype=torch.float64)print(x.requires_grad)# 第?種?式: 對代碼進?裝飾with torch.no_grad():y = x ** 2print(y.requires_grad)# 第?種?式: 對函數進?裝飾@torch.no_grad()def my_func(x):return x ** 2print(my_func(x).requires_grad)# 第三種?式torch.set_grad_enabled(False)y = x ** 2print(y.requires_grad)# 2. 注意: 累計梯度
def test02():# 定義需要求導張量x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)for _ in range(3):f1 = x ** 2 + 20f2 = f1.mean()# 默認張量的 grad 屬性會累計歷史梯度值# 所以, 需要我們每次?動清理上次的梯度# 注意: ?開始梯度不存在, 需要做判斷if x.grad is not None:x.grad.data.zero_()f2.backward()print(x.grad)# 3. 梯度下降優化最優解
def test03():# y = x**2x = torch.tensor(10, requires_grad=True, dtype=torch.float64)for _ in range(5000):# 正向計算f = x ** 2# 梯度清零if x.grad is not None:x.grad.data.zero_()# 反向傳播計算梯度f.backward()# 更新參數x.data = x.data - 0.001 * x.gradprint('%.10f' % x.data)if __name__ == '__main__':test01()# print('--------------------')# test02()# print('--------------------')# test03()
這里得分開打印,就不在展示結果了,大家打印一下看看。
四.梯度計算注意
當對設置 requires_grad=True 的張量使? numpy 函數進?轉換時, 會出現如下報錯:
Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.?
此時, 需要先使? detach 函數將張量進?分離, 再使? numpy 函數.?
注意: detach 之后會產??個新的張量, 新的張量作為葉?結點,并且該張量和原來的張量共享數據, 但是分 離后的張量不需要計算梯度。?
import torch# 1. detach 函數?法
def test01():x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)# Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.# print(x.numpy()) # 錯誤print(x.detach().numpy()) # 正確# 2. detach 前后張量共享內存
def test02():x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)# x2 作為葉?結點x2 = x1.detach()# 兩個張量的值?樣: 140421811165776 140421811165776print(id(x1.data), id(x2.data))x2.data = torch.tensor([100, 200])print(x1)print(x2)# x2 不會?動計算梯度: Falseprint(x2.requires_grad)if __name__ == '__main__':test01()test02()
結果展示:?
[10. 20.]
1834543349008 1834543349008
tensor([10., 20.], dtype=torch.float64, requires_grad=True)
tensor([100, 200])
False?
五.總結?
本?節主要講解了 PyTorch 中?常重要的?動微分模塊的使?和理解。我們對需要計算梯度的張量需要設 置 requires_grad=True 屬性,并且需要注意的是梯度是累計的,在每次計算梯度前需要先進?梯度清零。