1. 程式人生 > 其它 >動手學習深度學習筆記——PyTorch版(一)

動手學習深度學習筆記——PyTorch版(一)

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為梯度係數
    • \(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)
  • 向量對向量求導的自動求導示例——雅可比矩陣

\[y=[y_1,y_2,y_3]=[x_1^2+2x_2, x_2^2+4x_1, x_3^2+2x_1+x_2] \\\\ x=[x_1,x_2,x_3]=[1,2,3] \\\\ Jacobian = \begin{bmatrix} \frac{\partial y_1}{\partial x_1},\frac{\partial y_2}{\partial x_1},\frac{\partial y_3}{\partial x_1} \\ \frac{\partial y_1}{\partial x_2},\frac{\partial y_2}{\partial x_2},\frac{\partial y_3}{\partial x_2} \\ \frac{\partial y_1}{\partial x_3},\frac{\partial y_2}{\partial x_3},\frac{\partial y_3}{\partial x_3} \end{bmatrix} = \begin{bmatrix} 2x_1&4&2\\ 2&2x_2&21\\ 0&0&2x_3 \end{bmatrix} = \begin{bmatrix} 2&4&2\\ 2&4&1\\ 0&0&6 \end{bmatrix} \]
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.]])