Pytorch 微分
安裝套件
請先安裝如下套件
函數
假設函數為 $(f(x)=0.6x^2+5)$
圖形
如下代碼可畫出此函數之圖型

單點微分
函數微分後為 $(f'(x)=0.6*2x )$。當 x = 2 時,$(f'(2)= 0.6 *2 * 2 = 2.4 )$。如下代碼可計算 x = 2 時的導數。
- 函數 : y = 0.6 * torch.square(x) + 5,設定函數為 $(f(x) = 0.6 x ^2 +5)$
- 微分點 : x = torch.tensor([2.0]),求 x = 2.0 時的導數,一定要用浮點數,整數無法微分。
- $(\frac{dy}{dx})$ : y 對 x 的微分,使用 requires_grad = True 宣告 x 是要微分的對像。
- 微分 : y.backward(),開始計算 x = 2.0 時的微分值,結果置於 x.grad 中
多點微分
如果要計算多點 x 分別在 [1., 2., 3., 4.] 這 4 處的導數,x 就需宣告如下。
x = torch.tensor([1., 2., 3., 4.], requires_grad = True)
微分後的 x.grad 4 個值 [1.2000, 2.4000, 3.6000, 4.8000] 會再跟 y 的向量相乘,所以需在 y.backward() 裏需設定 4 個向量值,如下所示。
y.backward(torch.tensor[1,1,1,1])
權重
x 導數跟 y 的向量相乘,好像是多此一舉,但看了下面的代碼,結果都會接近 4.8,這就是權限的概念
import torch
x=torch.tensor([1.,2.,3.,4.], requires_grad=True)
y=0.6 * torch.square(x) + 5
y.backward(torch.tensor([3.99,1.97,1.35,1]))
print(x.grad)
結果:
tensor([4.7880, 4.7280, 4.8600, 4.8000])
為了讓結果都接近 4.8,所以 y 的向量要填入不同的值,這些值就稱為權重(weight)。模型的訓練,絕大多數都是在計算這些權重(怎麼算,就先不用管)。
上百點的需求
如果要計算上百、千個 x 點的導數,在 y.backward() 裏要輸入 torch.tensor([1,1,1,1…….]) 輸入 100、1000 個 1,那簡直要人命,所以可以用 torch.ones_like(y) 是建立一個跟 y 一模一樣形狀的 tensor。完整代碼如下
import torch x = torch.tensor([1.,2.,3.,4.], requires_grad=True) y = troch.square(x) y.backward(torch.ones_like(y)) print(x.grad) 結果 : tensor([2., 4., 6., 8.])
更直覺的方法
torch.ones_like(y) 蠻怪異的,所以可以變成如下比較好理解的方式,二者一模一樣。
import torch
x=torch.tensor([1.,2.,3.,4.], requires_grad=True)
y=0.6 * torch.square(x) + 5
y.sum().backward()
print(x.grad)
$(\sum)$微分
假設 $(f(x)=\sum_{i=1}^{4}a_ix^2+b_i )$,a 是 [0.1, 0.2, 0.3, 0.4] 的集合,b 是 [1, 2, 3, 4] 的集合。
$(f'(x)=\sum_{i=1}^{4}2a_ix = 2(0.1+0.2+0.3+0.4)x=2x)$,所以當 x = 2 時的微分值是 2*2 = 4,相關代碼如下
偏微分
偏微分其實只考慮單一層面的影響。比如我是賣便當的,影響我淨利的有菜價、人工成本、電價、瓦斯價、房屋租金。我現在只想知道,電價上升一塊錢,會影響我多少收益,這稱為斜率,也就是電價對淨利的偏微分。
假設公式為 $(f(x,y)=\sum_{i=1}^{n}a_ix^2+b_iy^2)$,a 是 [0.1, 0.2, 0.3, 0.4] 的集合,b 是 [1, 2, 3, 4] 的集合。
對 x 的偏微分為 $(\frac{f(x, y)}{\sigma x}=\sum_{i=1}^{4}2a_ix)$,其意是 x 升 1 單位,z 升多少。
對 y 的偏微分為 $(\frac{f(x, y)}{\sigma y}=\sum_{i=1}^{4}2b_iy)$,其意是 y 升 1 單位,z 升多少。
梯度下降
假設要使用梯度下降計算 $(y=x^2)$ 的極小值,x 一開始由 -5 開始逼近,經由基礎梯度下降的說明,每次逼近的公式為$(x_{t+1}=x_{t} – f'(x_t) * lr)$
先定義 x 張量,切記一定要寫 5. ,因為只有小數才能進行微分。
x=torch.tensor([-5.], requires_grad=True)
定義公式 $(y = x^2)$,這個公式在每次 x 變更時都需重新定義,所以一定要寫在迴圈中,然後用 y.backward() 進行微分。
因為 x 是標量,所以 y.backward() 不需給存放的位置
for i in range(epochs):
y=x.square()
y.backward()
接下來逼近 x ,但請注意 x 值只要一改變,就會被自動追蹤計算的過程,所以手動更改 x 值時,一定要放在 with torch.no_grad() 區塊中,這個目的是在告知 pytorch,區塊中的計算請不要追蹤。
如果不放在區塊中,只能重定 x = torch.tensor([ x – x.grad * lr], requires_grad = True),但這會影響效能。
另外在區塊中,只能寫 x -= …. ,如果寫成 x = x – x.grad * lr,是會出現錯誤的。
with torch.no_grad():
x -= x.grad * lr#不可以寫成 x = x - x.grad *0.01
最後一定要清除 x.grad,如果不清除,前後的結果會累加進去
x.grad.zero_()
完整代碼如下
detach
torch 的張量模式有時需轉成 numpy 格式進行計算或繪圖,此時可以用 .numpy() 進行轉換。
但張量若宣告為 requires_grad = True 時,在這狀態下處於追蹤模式,是不能轉成 numpy 格式的,必需使用 detach() 暫時分離追蹤模式。
如下代碼,x 宣告為追蹤模式,y 也隨之變成追蹤模式,所以都要先 detach 後才能轉成 numpy 格式,z 就不用分離了。
import torch
x=torch.tensor([1.,2.,3.,4.], requires_grad=True)
y=x.square()
z=torch.tensor([1.,2.,3.,4.])
print(x.detach().numpy())
print(y.detach().numpy())
print(z.numpy())
結果 :
[1. 2. 3. 4.]
[ 1. 4. 9. 16.]
[1. 2. 3. 4.]
torch.no_grad區塊
在 torch.no_grad 區塊是屬於分離狀態的,所以可以直接使用 numpy() 轉換,但如果脫離了區塊範圍,就要加上 detach() 方法
底下代碼藍色部份,分別是區塊內及區塊外的使用方法。
GPU運算
如果交由 GPU運算,要將 GPU 裏的變數轉成 numpy,需使用 .cpu() 將變數 copy 到主機版的 Ram, 後再轉成 numpy()。底下代碼藍色部份,分別是區塊內及區塊外的使用方法。
