動手學習深度學習筆記——PyTorch版(一)
阿新 • • 發佈:2022-03-05
torch基本操作
import torch # 建立 x = torch.arange(12, dtype=torch.float32) # 建立值為0-11的向量 y = torch.tensor([[1,2,3],[4,5,6]]) # 通過陣列建立張量 torch.zeros((2,3,4)) # 形狀為 2*3*4 的全0陣列 torch.ones((2,3,4)) # 形狀為 2*3*4 的全1陣列 # 操作 x.shape # 檢視張量形狀 type(x) # 檢視x的資料型別 x.numel() # 檢視張量元素個數 x = x.reshape(3, 4) # 改變張量形狀 1*12 改為 3*4 torch.cat((x,y), dim=0) # 按第0維連結張量 x.type(torch.float) # 轉換資料型別 # 計算:加減乘除,冪運算,指數運算 —— 均可按元素計算,可通過廣播機制計算 # 廣播機制:用於計算的張量形狀如果對應某一個維度, 缺失的維度會自動複製填充 x+y, x-y,x*y,x/y,x**y, torch.exp(x) # 生成邏輯張量 —— 必須對應一個或所有維度 x == torch.tensor([[0, 4, 5]]) x == torch.tensor([[1], [3]]) x == 2 x == torch.arange(12, dtype=torch.float32) # 元素求和 —— 可按照多維, 結果形狀相當於去除進行求和的維度 x.sum(axis=0) x.sum(axis=[0, 1]) x.cumsum(axis=0) # 累加求和 # 求均值 x.mean(axis=0) # 記憶體 id(x) # 類似c的指標 x = x+1 # x的id會改變 x += 1 # x的id不變 —— 減少記憶體開銷 x[:] = 1 # x的id不變 # 建立形狀相同張量 z = torch.zeros_like(x) # 建立一個大小與x相同的張量 # torch轉換為numpy陣列 z = x.numpy() # numpy陣列轉torch torch.tensor(z) # 大小為1的張量轉python標量 a = torch.tensor([10]) a.item()
資料預處理
資料生成
import os os.makedirs(os.path.join('.', 'data'), exist_ok=True) # 建立資料夾 exist_ok=True表示檔案存在時繼續 data_file = os.path.join('.', 'data', 'test.csv') # 建立檔案 with open(data_file, 'w') as f: f.write('a,b,c\n') # 列名 f.write('NA,PC,362\n') # 以下為各行資料 f.write('4,NA,3110\n') f.write('5,NA,35\n') f.write('NA,NA,31\n') f.close()
資料預處理
import os import pandas as pd data_file = os.path.join('.', 'data', 'test.csv') data = pd.read_csv(data_file) print(data) # 處理缺失資料 —— 常用刪除和差值,以下通過平均值插值 inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2] # 通過index location 分離首行和資料 inputs = inputs.fillna(inputs.mean()) # 通過平均值填充(非數值型不會填充) # 將非數值單獨分列(通過0-1表示),dummy_na=True 表示非數值列的NAN資料也單獨分成一列 inputs = pd.get_dummies(inputs, dummy_na=True) # inputs.values 將 pandas 格式轉為 numpy 陣列格式,之後將其轉為張量 X, y = torch.tensor(inputs.values), torch.tensor(outputs.values) X, y
線性代數
- 矩陣相乘代表一種空間扭曲
# 線性代數的實現
A = torch.arange(20).reshape(4,5)
B = torch.arange(15).reshape(5,3)
x = torch.arange(5)
A.T # 矩陣的轉置
A.clone() # 得到A的副本,如果使用B=A,id(A)==id(B)
# 向量點積,
x.dot(x)
torch.dot(x, x)
torch.sum(x*x)
# 矩陣與向量點積
torch.mv(A,x)
# 矩陣與矩陣點積
torch.mm(A,B)
# L1範數 —— 向量元素絕對值求和
torch.abs(u).sum()
# L2範數 —— 向量元素平方和的平方根
u = torch.tensor([3.0, 4.0])
torch.norm(u)
# 矩陣的 弗羅貝尼烏斯範數 —— 元素平方和的平方根
torch.norm(A.type(torch.float))
微積分
- 求導
- 標量求導
- 向量求導——梯度
- 矩陣求導
標量對向量求導
向量對向量求導
例一
例二
向量鏈式求導法則
正向累積,反向累計(反向傳遞)
計算圖
顯式構造
- 有變數 a、b,公式 c = a + b;=> a=1,b=2 時,c=3
隱式構造
- 系統先記住 a=1,b=2;=> 有公式 c = a + b,則 c=3
正向累積:計算原函式結果和中間值
- 時間複雜度O(n):操作子個數
- 空間複雜度O(1)
反向傳遞:計算梯度
- 時間複雜度O(n):操作子個數
- 空間複雜度O(n):需要先進行正向累積儲存所有中間結果(耗資源)
自動求導實現
import torch
x = torch.arange(4.0, requires_grad=True) # requires_grad 用於儲存梯度
# x = torch.arange(4.0)
# x.requires_grad_(True) # 同等效果
x.grad == None # 初始預設為None
y = 2 * torch.dot(x, x) # 隱式構造計算圖並執行 —— grad_fn
y.backward() # 隱式呼叫反向傳播函式自動計算y關於x每個分量的梯度
x.grad # 結果,預設累計梯度
x.grad.zero_() # 清零之前的值
y = x.sum() # 新函式
y.backward()
x.grad
-
y.backward()
- 若結果非標量
- 通過 sum,例:
y.sum().backward()
- 顯式呼叫 例:
y.backward(torch.tensor([1, 1]))
,1為梯度係數
- 通過 sum,例:
- \(y=x1+x2 => \frac{dy}{dx}=\frac{dx1}{dx}+\frac{dx2}{dx} => [\frac{dx1}{dx}, \frac{dx2}{dx}]\)
- 預設執行後清空中間值,所以如果需要多次使用,需要加上
retain_graph=True
儲存y.backward(retain_graph=True)
- 若結果非標量
-
向量對向量求導的自動求導示例——雅可比矩陣
import torch
x = torch.tensor([[1.0, 2, 3]], requires_grad=True)
Jacobian = torch.zeros(3, 3)
y = torch.zeros(1, 3)
y[0, 0] = x[0, 0] ** 2 + 2 * x[0, 1]
y[0, 1] = x[0, 1] ** 2 + 4 * x[0, 0]
y[0, 2] = x[0, 2] ** 2 + 2 * x[0, 0] + x[0, 1]
y.backward(torch.tensor([[1, 0, 0]]), retain_graph=True) # 第一列
Jacobian[:, 0] = x.grad
x.grad.zero_()
y.backward(torch.tensor([[0, 1, 0]]), retain_graph=True) # 第二列
Jacobian[:, 1] = x.grad
x.grad.zero_()
y.backward(torch.tensor([[0, 0, 1]]), retain_graph=True) # 第三列
Jacobian[:, 2] = x.grad
x.grad.zero_()
print(Jacobian)
# tensor([[2., 4., 2.],
# [2., 4., 1.],
# [0., 0., 6.]])