【Pytorch 深度學習筆記】搞懂什麼是深度學習以及 Pytorch 的基礎

哈囉大家好我是 LukeTseng,感謝您點進本篇筆記,該篇筆記主要配合讀本 《Deep Learning with pytorch》 進行學習,另外透過網路資料作為輔助。本系列筆記是我本人奠基深度學習基礎知識的開始,若文章有誤煩請各位指正,謝謝!

深度學習(Deep Learning, DL)是什麼?

深度學習(Deep Learning)是機器學習的一個分支,是一種以人工神經網路為架構、對資料進行表徵學習的演算法。比較通俗一點的說法是說,深度學習可以透過模仿人類大腦的神經網路,讓機器能像人腦一樣進行學習。

至於「深度」這個詞指的是在網路中使用了多層結構,這些多層結構能夠處理更複雜的特徵和模式。

機器學習 vs 深度學習

《Deep Learning with pytorch》這本書中提到深度學習的變革,以及為什麼需要深度學習。

過去機器學習使用的傳統方法高度依賴於特徵工程(feature engineering)這個技術,也就是人工輸入資料的轉換方式,以利於下游演算法產生正確結果。(以下 google 的網站可以讓你玩一下機器學習,玩完後就會知道這句話是什麼意思了:https://teachablemachine.withgoogle.com)

比如說要辨別出手寫的數字,像 0 跟 1,工程師會手動設計出一套過濾器(filters),用於估計圖像中的邊緣方向(direction of edges)。

邊緣方向是什麼呢?邊緣方向是一種用來區分圖像中不同形狀或結構的特徵。以便區分 1(通常是垂直或接近垂直的邊緣)和 0(包含封閉的圓形邊緣)。

在辨別手寫數字的例子呢,另個有用的手動設計特徵可能是計算封閉孔洞(enclosed holes)的數量,因為 0, 8 以及有圈的 2 等數字中都存在此類特徵,可以提高識別率。

最後就是訓練分類器(classifier),這些手動提取的特徵隨後會被饋送給一個 classifier,classifier 會根據這些特徵的分佈來預測正確的數字。

看到這裡呢,所以機器學習他基本上就是要純手動的去輸入資料、轉換特徵,那這樣子效率不是很低嗎?但其實機器學習可以在金融業做到數據分析,或是其它像推薦引擎、天氣預測等應用。


而深度學習則帶來了範式轉移(paradigm shift, 機器學習可稱為舊的範式, 深度學習則是新的範式),它能夠從原始數據中自動學習特徵。

一樣是區分 0, 1 手寫數字的例子,深度學習中的過濾器(filters)會在訓練過程中被迭代地提煉 (refined)。ummm… 也就是用 for 迴圈一直讓他去跑 training 的意思。

訓練過程會使用一個準則(criterion,即損失函數 Loss function),該函數提供一個數值分數來衡量模型輸出與參考數據(期望輸出)之間的差異。訓練的目標是透過逐步修改深度學習機器,讓分數越來越低,以此確保在對於訓練期間未見過的數據也能維持低分數。

深度學習可以根據範例去學習,這種 refined 是透過不斷讓機器去看範例圖像(如 0, 1 的數字照片)及其目標標籤對(pairs of examples and target labels)來實現的。

:::info
標籤對(labeled pairs)是指「輸入資料」和它對應的「正確答案」配對在一起的組合。在機器學習中,標籤(label)是指一個資料樣本的正確輸出或類別,也就是想要模型預測的目標變數。
:::

小結

機器學習深度學習
特徵提取需要人工設計特徵工程自動從原始數據學習特徵
人工介入需要大量人工指導和調整模型可自主判斷優化
處理數據類型適合結構化數據擅長非結構化數據(圖像、音訊、文本)
硬體需求相對較低需要大量數據和算力

基本上可以把深度學習看成是機器學習的 Pro 版,但缺點就是需要龐大的 data 去做訓練才會提高準確率。

為什麼要用 Pytorch?

Pytorch 他是目前最流行的開源深度學習框架之一,因其易用性、靈活性和強大的功能而廣受歡迎。自 2019 年起,超過 75% 的國際學術論文都使用了 Pytorch,所以就可見 Pytorch 它的強大了。

另外 Pytorch 可以做到部分實時的執行跟測試,而非等待整個 code 跑完,這對於大型深度學習模型來說,完整執行可能需要很長一段時間。所以 Pytorch 是個很好去快速設計原型的平台,而且大幅加速了 debug 的流程。

在書中也提到 Pytorch 是基於 Python 去構建的,所以具有 Pythonic 的風格。說歸說 Pytorch 是基於 Python 去構建的東西,但其實底層還是由許多 C++ 跟 CUDA,因為考量到效能的關係,所以得讓這些程式碼在 GPU 上大規模執行。

總之 Pytorch 還有相當多的優點,僅列上述就已經足夠證明為何需要使用 Pytorch 了。

Pytorch 的基礎概念

Pytorch 的基礎概念主要是以下這五個東西所組成:Tensor、Autograd、nn.Module、Optimizers 和 Device。

Tensor(張量)

image

Image Source:https://www.newittrendzzz.com/post/understanding-tensors-in-pytorch-a-comprehensive-guide

Tensor 是 PyTorch 的資料結構,它是一種包含多個數值的數學物體,可以視為向量和矩陣的廣義化版本。tensor 支援多維陣列的運算,並可以在 CPU 或 GPU 上進行加速計算。

張量具有三個屬性:

  • 維度(Dimensionality):0 維是純量、1 維是向量、2 維是矩陣、3 維以上是高維張量。
  • 形狀(Shape):每個維度上的大小,例如 (3, 4) 表示 3 行 4 列。
  • 資料型態(Dtype):如 torch.float32torch.int64 等。

以下是 pytorch 的範例(建議於 colab 或是 jupyter notebook 上面實作):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torch

# 創建不同維度的張量
scalar = torch.tensor(3.14) # 0 維:純量
vector = torch.tensor([1, 2, 3]) # 1 維:向量
matrix = torch.tensor([[1, 2], [3, 4]]) # 2 維:矩陣

# 查看張量屬性
print(matrix.shape) # torch.Size([2, 2])
print(matrix.dtype) # torch.int64

# 查看張量
print(scalar)
print(vector)
print(matrix)

Output:

1
2
3
4
5
6
torch.Size([2, 2])
torch.int64
tensor(3.1400)
tensor([1, 2, 3])
tensor([[1, 2],
[3, 4]])

tensor 跟 numpy 的 ndarray 蠻像、蠻類似的,但差別在於 tensor 可以在 GPU 上執行,而 numpy 只能在 CPU 上執行。

另外 pytorch 也可以直接從 numpy 陣列中創建 tensor:

1
2
3
4
5
import torch
import numpy as np
arr = np.array([[1, 2], [3, 4]])
tensor_from_numpy = torch.from_numpy(arr)
print(tensor_from_numpy)

Output:

1
2
tensor([[1, 2],
[3, 4]])

實際上建立一個 tensor 並不只有兩種形式,以下列出所有建立 tensor 的方法:

methodsdescriptionexample code
torch.tensor(data)從 Python 列表或 NumPy 陣列建立張量。x = torch.tensor([[1, 2], [3, 4]])
torch.zeros(size)建立一個全為 0 的張量。x = torch.zeros((2, 3))
torch.ones(size)建立一個全為 1 的張量。x = torch.ones((2, 3))
torch.empty(size)建立一個未初始化的張量。x = torch.empty((2, 3))
torch.rand(size)建立一個服從均勻分佈的隨機張量,值在 $[0, 1)$ 。x = torch.rand((2, 3))
torch.randn(size)建立一個服從常態分配的隨機張量,平均值為 0,標準差為 1。x = torch.randn((2, 3))
torch.arange(start, end, step)建立一個一維序列張量,類似 Python 的 range。x = torch.arange(0, 10, 2)
torch.linspace(start, end, steps)建立一個在指定範圍內等間隔的序列張量。x = torch.linspace(0, 1, 5)
torch.eye(size)建立一個單位矩陣(對角線為 1,其他為 0)。x = torch.eye(3)
torch.from_numpy(ndarray)將 NumPy 陣列轉換為張量。x = torch.from_numpy(np.array([1, 2, 3]))

table from PyTorch 张量(Tensor) | 菜鸟教程

Tensor 的屬性

屬性名稱說明回傳型態範例
shapesize()張量的維度大小torch.Sizetorch.Size([^11][^12])
dtype數據類型torch.dtypetorch.float32, torch.int64
device儲存設備(CPU/GPU)torch.devicecpu, cuda:0
requires_grad是否需要計算梯度boolTrueFalse
ndim維度數量int2(表示二維張量)
numel()元素總數int12(3×4 矩陣)
data底層數據(不追蹤梯度)Tensor原始數據張量
grad梯度值Tensor 或 None反向傳播後的梯度
layout內存布局類型torch.layouttorch.strided
T轉置(2D 張量)Tensor行列互換的張量
is_cuda是否在 GPU 上boolTrueFalse
dim()獲取張量的維度數inttensor.dim()
item()取得單元素張量的值numbertensor.item()
is_contiguous()檢查張量是否連續儲存booltensor.is_contiguous()

table from PyTorch 张量(Tensor) | 菜鸟教程

接下來的範例程式碼都會在 google colab 上執行。

shape or size():

1
2
3
4
5
import torch
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x.shape) # torch.Size([2, 3])
print(x.size()) # torch.Size([2, 3])
print(x.size(0)) # 2 (第一個維度的大小)
1
2
3
torch.Size([2, 3])
torch.Size([2, 3])
2

device:

1
2
3
4
5
6
x = torch.tensor([1, 2, 3])
print(x.device)

if torch.cuda.is_available():
y = x.to('cuda:0')
print(y.device)
1
2
cpu
cuda:0

requires_grad:

1
2
3
4
5
6
7
8
x = torch.tensor([1.0, 2.0, 3.0], requires_grad = True)
print(x.requires_grad)

y = torch.tensor([1.0, 2.0, 3.0])
print(y.requires_grad)

y.requires_grad = True # 可手動設定
print(y.requires_grad)
1
2
3
True
False
True

requires_grad 的作用是讓 backward 可以決定了該張量是否要加入自動微分(Autograd)機制。

tensor.requires_grad = True 時,代表該 tensor 在向前運算(forward pass)時,其所有用於產生它的運算都會被記錄下來,之後呼叫 tensor.backward() 時,就會自動計算該張量關於其葉張量(leaf tensor)的梯度(gradient)值。

ndim:

1
2
3
4
5
6
7
8
x = torch.tensor([1, 2, 3])
print(x.ndim)

y = torch.tensor([[1, 2], [3, 4]])
print(y.ndim)

z = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(z.ndim)
1
2
3
1
2
3

1 表示 1 維張量,2 表示 2 維張量,以此類推。

numel():

1
2
3
4
5
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x.numel())

y = torch.ones(3, 4, 5)
print(y.numel())
1
2
6
60

這個函數主要用來表示裡面有多少個元素,輸出第一個的 6 就表示 x 裡面有 6 個元素。

data:

1
2
3
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x * 2
print(y.data)
1
tensor([2., 4., 6.])

grad:

1
2
3
4
5
6
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = (x ** 2).sum()
print(x.grad) # None (反向傳播前)

y.backward()
print(x.grad) # tensor([2., 4., 6.]) (反向傳播後的梯度)
1
2
None
tensor([2., 4., 6.])

layout:

1
2
x = torch.tensor([[1, 2], [3, 4]])
print(x.layout)
1
torch.strided

torch.strided 為預設的密集儲存格式,若遇到稀疏 tensor 會有不同的 layout。

T:

1
2
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x.T)
1
2
3
tensor([[1, 4],
[2, 5],
[3, 6]])

T 只適用於 2D 張量,對於高維張量需用 permute()transpose() 函數。

is_cuda:

1
2
3
4
5
6
x = torch.tensor([1, 2, 3])
print(x.is_cuda)

if torch.cuda.is_available():
y = x.cuda()
print(y.is_cuda)
1
2
False
True

dim():

1
2
3
4
5
6
7
8
x = torch.tensor([1, 2, 3])
print(x.dim())

y = torch.tensor([[1, 2], [3, 4]])
print(y.dim())

z = torch.ones(2, 3, 4)
print(z.dim())
1
2
3
1
2
3

ndim 跟 dim() 使用上是沒什麼差別的,差別在於 ndim 是比較新的版本才出來的,他是屬於屬性的部分,而非方法。

item():

1
2
3
4
5
6
7
x = torch.tensor([42])
print(x.item())

y = torch.tensor([3.14])
print(y.item())

# 只能用於單元素張量,多元素會報錯
1
2
42
3.140000104904175

is_contiguous():

1
2
3
4
5
6
7
8
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x.is_contiguous()) # True (原始張量是連續的)

y = x.transpose(0, 1)
print(y.is_contiguous()) # False (轉置後不連續)

z = y.contiguous()
print(z.is_contiguous()) # True (使用 contiguous() 使其連續)

Tensor 的操作

數學運算操作:

操作功能說明語法範例說明
加法元素相加x + ytorch.add(x, y)對應位置相加
減法元素相減x - ytorch.sub(x, y)對應位置相減
乘法(元素)元素相乘x * ytorch.mul(x, y)Hadamard 積
除法元素相除x / ytorch.div(x, y)對應位置相除
矩陣乘法矩陣相乘x @ ytorch.mm(x, y)線性代數的矩陣乘法
matmul()多維矩陣乘法torch.matmul(x, y)支援批次矩陣乘法
pow()次方運算x.pow(2)x ** 2每個元素平方
sqrt()平方根torch.sqrt(x)每個元素開根號
exp()指數函數torch.exp(x)$e^x$
log()自然對數torch.log(x)$\ln(x)$
abs()絕對值torch.abs(x)每個元素取絕對值
sin/cos/tan三角函數torch.sin(x)三角運算

統計操作:

操作功能說明語法範例說明
sum()求和x.sum()x.sum(dim=0)可指定維度
mean()平均值x.mean()x.mean(dim=1)需要浮點數類型
max()最大值x.max()x.max(dim=0)回傳值和索引
min()最小值x.min()最小元素
argmax()最大值索引x.argmax(dim=1)回傳最大值位置
argmin()最小值索引x.argmin()回傳最小值位置
std()標準差x.std()衡量數據分散程度
var()變異數x.var()標準差的平方
median()中位數torch.median(x)中間值

比較操作:

操作功能說明語法範例返回結果
大於元素比較x > ytorch.gt(x, y)布林張量
小於元素比較x < ytorch.lt(x, y)布林張量
等於元素比較x == ytorch.eq(x, y)布林張量
大於等於元素比較x >= ytorch.ge(x, y)布林張量
小於等於元素比較x <= ytorch.le(x, y)布林張量
equal()完全相等torch.equal(x, y)單一布林值
allclose()近似相等torch.allclose(x, y)考慮浮點誤差

形狀變換操作:

操作功能說明語法範例實際例子
reshape()改變形狀(複製數據)x.reshape(3, 4)把 12 個元素重排成 3×4
view()改變形狀(共享記憶體)x.view(2, -1)-1 表示自動計算
squeeze()移除大小為 1 的維度x.squeeze()(1, 3, 1, 4)(3, 4)
unsqueeze()增加維度x.unsqueeze(0)(3, 4)(1, 3, 4)
transpose()交換兩個維度x.transpose(0, 1)交換第 0 和第 1 維
permute()重新排列所有維度x.permute(2, 0, 1)將維度重新排序
flatten()展平為一維x.flatten()所有元素變成一維向量
expand()擴展張量(不複製)x.expand(3, -1)廣播機制擴展
repeat()重複張量(複製)x.repeat(2, 3)沿各維度重複

在 tensor 上做 GPU 加速

在前面的範例有稍微介紹到 GPU 加速的部分,這邊額外講解。

要在做 GPU 加速之前,需要透過函數 torch.cuda.is_available() 確認是否有可用的 cuda GPU。

torch.cuda.device_count() 可查看 GPU 數量。

torch.cuda.get_device_name(0)查看 GPU 名稱。

做 GPU 加速的第一個方式是用 .cuda() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
x = torch.tensor([1.0, 2.0, 3.0])
print(x.device) # cpu

# 移動到 GPU(回傳新的 tensor)
x_gpu = x.cuda()
print(x_gpu.device) # cuda:0

# 或者重新賦值給自己
x = x.cuda()
print(x.is_cuda) # True

# 指定特定的 GPU(如果有多張顯卡)
x = x.cuda(0) # 使用第一張 GPU
1
2
3
cpu
cuda:0
True

第二個方式比較推薦用,是使用 .to(device) 方法:

1
2
3
4
5
6
7
8
9
10
11
12
# 定義設備(自動選擇 GPU 或 CPU)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device) # cuda:0 或 cpu

# 建立 tensor 並移動到指定設備
x = torch.tensor([1.0, 2.0, 3.0])
x = x.to(device)
print(x.device) # cuda:0

# 指定特定 GPU
device = torch.device("cuda:0") # 第一張 GPU
x = x.to(device)

最後是比較 CPU 與 GPU 間的速度的小範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import time

# 創建大型矩陣
x = torch.randn(10000, 10000)
y = torch.randn(10000, 10000)

# CPU 計算時間
start = time.time()
z_cpu = x @ y # 矩陣乘法
print(f"CPU 時間: {time.time() - start:.4f} 秒")

# GPU 計算時間
x_gpu = x.cuda()
y_gpu = y.cuda()
torch.cuda.synchronize() # 確保 GPU 操作完成
start = time.time()
z_gpu = x_gpu @ y_gpu
torch.cuda.synchronize()
print(f"GPU 時間: {time.time() - start:.4f} 秒")
1
2
CPU 時間: 24.7626 秒
GPU 時間: 0.5167 秒

所以知道為什麼要用 GPU 了吧 XD,尤其日後當層數與參數量大起來的時候,就特別需要 GPU 做運算了。

總結

PyTorch 的五大基礎概念

Tensor(張量)

PyTorch 的核心資料結構,為多維陣列的泛化形式(類似 NumPy ndarray),可在 CPU 或 GPU 上運算。

  • 0 維 → 純量(scalar)
  • 1 維 → 向量(vector)
  • 2 維 → 矩陣(matrix)
  • 3 維以上 → 高維張量

Autograd(自動微分)

透過 requires_grad=True 追蹤張量的運算過程,反向傳播時自動計算梯度(gradient)。

nn.Module(神經網路模組)

建構深度學習模型的基底類別,可封裝多層結構(如 CNN、RNN)。

Optimizers(優化器)

用於更新模型參數(如 SGD、Adam),以降低損失函數。

Device(裝置)

控制資料與模型運算位置(cpu 或 cuda)。

Tensor 操作

  1. 建立張量的方法

如 torch.tensor()、torch.zeros()、torch.ones()、torch.rand()、torch.eye() 等,支援從列表或 NumPy 陣列建立。

  1. 張量屬性
  • shape / size():張量形狀
  • dtype:資料型態
  • device:儲存裝置
  • requires_grad:是否參與梯度計算
  • grad:梯度值
  • is_cuda:是否位於 GPU
  1. 常用運算
  • 數學運算(加減乘除、平方、log、sin 等)、
  • 統計運算(sum、mean、std、var 等)、
  • 比較運算(==, >, torch.eq() 等)。
  1. 張量形狀操作

reshape()、view()、squeeze()、unsqueeze()、transpose()、permute()、flatten()、expand()、repeat() 等。

GPU 加速運算

在深度學習中,GPU 能顯著加速矩陣與張量運算。
主要有兩種方式將資料移動到 GPU:

  • .cuda():直接將 tensor 移到 GPU。
  • .to(device):推薦用法,可自動偵測 CPU/GPU。

參考資料

書籍《Deep Learning with PyTorch》。

PyTorch documentation — PyTorch 2.9 documentation

关于tensor.dim() 和 tensor.ndim-CSDN博客

Pytorch学习: 张量基础操作 | DaNing的博客

[PyTroch系列-10]:PyTorch基础 - 张量Tensor元素的比较运算_51CTO博客_pytorch中的张量

numpy & pytotch tensor 常用操作对比_to(dtype).clone().detach().contiguous()-CSDN博客

cuda pytorch 加速 pytorch怎么用gpu加速_mob6454cc75556b的技术博客_51CTO博客

PyTorch 笔记(19)— Tensor 用 GPU 加速_tensor.cuda()-CSDN博客

PyTorch 基础 | 菜鸟教程

PyTorch 张量(Tensor) | 菜鸟教程