近期在看李沐大神的動手學深度學習課程,在此,記錄一些重要知識點,不要讓知識到處飄,讓知識彙總起來。
1、PyTorch 的 backward 必須是標量#
import torch
x = torch.arange(4.0,requires_grad=True)
y = 2 * torch.dot(x,x)
y.backward()
# 默認情況下,PyTorch會累積梯度,需要清除之前的值
# 對非標量調用 'backward' 需要傳入一個 'gradient' 參數,該參數指定微分函數
x.grad.zero_() # 清除之前x的梯度
y = x * x # 這裡的y不是一個標量,這是一個向量
print(y)
# 等價於y.backward(torch.ones(len(x)))
y.sum().backward() # y.sum()後就講向量轉為標量了,對標量求導
x.grad
🔹 關鍵點:PyTorch 的 backward 必須是標量
在 PyTorch 中,.backward()
的設計是基於 鏈式法則,主要針對標量(單個輸出值)對輸入的梯度。
如果你調用 y.backward()
,這裡的 y 必須是一個標量,因為:
-
叫做 y 關於 x 的梯度(gradient)
-
是 y 對 x 的偏導數
但是到底它具體是什麼,取決於 $y$ 和 $x$ 的維度。
🔸 情況 1:y 是標量,x 是向量
假設:
那麼:
也就是:
一個長度為 的向量,表示 y 對每個 x 分量的偏導數。
這正是機器學習裡最常用的情況,比如 ** loss 對權重的梯度 **。
🔸 情況 2:y 是向量,x 是向量
假設:
那麼:
這個矩陣 叫做 Jacobian 矩陣,它的 元素是:
2、Jacobian 矩陣#
Jacobian 矩陣是:
一個向量函數對輸入向量的偏導矩陣。
假設:
-
輸入:
-
輸出:
Jacobian:
直觀上:
-
第 行: 對所有 的偏導。
-
描述:輸入變量小變動對每個輸出變量的線性影響。
3、Hessian 矩陣#
Hessian 矩陣是:
一個標量函數對輸入向量的二階偏導矩陣。
假設:
-
輸入:
-
輸出:
Hessian:
但是當 是一個 向量,梯度其實是 Jacobian 矩陣,不是單個梯度向量:
4、pytorch 中 dtype 和 type 的區別#
先放結論:
dtype
是張量內元素的具體數值類型,type
是張量對象本身的 Python 類型(包含設備信息)。
它們關注的層面完全不一樣。
📍 1️⃣ dtype
是什麼?
-
指:張量裡每個元素的存儲類型。
-
舉例:
-
torch.float32
→ 單精度浮點數 -
torch.int64
→ 64 位整數 -
torch.bool
→ 布爾類型
-
看代碼:
x = torch.tensor([1, 2, 3], dtype=torch.float32)
print(x.dtype) # 輸出:torch.float32
它只告訴你「這個張量的元素用什麼格式存儲」。
📍 2️⃣ type
是什麼?
-
指:張量對象在 PyTorch 中的全名,包括數據類型和設備。
-
舉例:
-
torch.FloatTensor
→ float32 的 CPU 張量 -
torch.cuda.FloatTensor
→ float32 的 GPU 張量 -
torch.IntTensor
→ int32 的 CPU 張量
-
看代碼:
x = torch.tensor([1, 2, 3])
print(x.type()) # 輸出:torch.IntTensor(或你的系統默認類型)
它告訴你「這個張量對象在 PyTorch 系統裡的完整類型名」。
📍 ⚠ 關鍵區別
對比項 | dtype | type |
---|---|---|
關注點 | 元素的數值類型 | 張量對象的完整 PyTorch 類型(包括設備信息) |
舉例 | torch.float32, torch.int64 | torch.FloatTensor, torch.cuda.FloatTensor |
主要用途 | 精度、存儲、計算相關 | 區分不同張量類別,調試 / 檢查用 |
改變方式 | .to(dtype) ,.float() | .type() (更換整個類型對象) |
5、Soft Label vs Hard Label#
通常我們訓練分類模型,用的標籤是:
✅ hard label(硬標籤)
比如,對於 3 分類任務,真實標籤:
類別 A → [1, 0, 0]
類別 B → [0, 1, 0]
類別 C → [0, 0, 1]
這種標籤完全是 獨熱編碼 (one-hot),只認對與錯。
soft label(軟標籤) 則是:
每個類別對應一個概率,而不是硬性的 0 或 1。
比如:
對於某張圖片,soft label → [0.7, 0.2, 0.1]
這表示:
-
有 70% 概率是類別 A
-
20% 概率是類別 B
-
10% 概率是類別 C
換句話說,標籤也 “承認模糊性”,不是全或無。
6、softmax 回歸和 logistic 回歸#
** 📦 共同點 **
✅ 本質都是分類模型
✅ 都用線性函數 + 激活(sigmoid 或 softmax)
✅ 都用交叉熵(cross entropy)作為損失函數
但它們用在不同的任務上。
🌟 主要區別
項目 | Logistic 回歸 | Softmax 回歸 |
---|---|---|
任務類型 | 二分類(binary classification) | 多分類(multi-class classification) |
輸出層激活函數 | sigmoid(單值輸出 0~1) | softmax(向量輸出,每個類別概率) |
輸出維度 | 1 維 | C 維(C = 類別數) |
目標標籤 | 0 或 1 | one-hot 編碼,比如 [0,0,1,0] |
決策方式 | 輸出 > 0.5 判為正類 | 最大概率的類別作為預測結果 |
📊 Logistic 回歸細節
-
假設你有:
-
輸入特徵
-
權重 和偏置
-
-
模型計算:
其中 是 sigmoid 函數。
輸出 是一個概率值(0 到 1),代表正類的概率。
損失函數:
📊 Softmax 回歸細節
-
假設你有:
-
輸入特徵
-
權重矩陣 (shape: num_features × num_classes)
-
偏置
-
-
模型計算:
其中
輸出 $\hat {y}$ 是一個長度為 C 的向量,每個元素是對應類別的概率。
損失函數:
** 🧠 為什麼 logistic 回歸不能直接用在多分類?**
因為 sigmoid 只輸出一個值,而多分類需要輸出多個類別的概率,且這些概率要滿足:
總和為 1,互相排斥。
這就是 softmax 的設計目的。
✅ 總結一句話
Logistic 回歸 ≈ 2 類 softmax 特例
Softmax 回歸 = 多分類推廣版 logistic 回歸
7、似然和概率#
🌟 什麼是似然函數?
簡單說:
似然函數(likelihood function)是給定模型參數下,觀察到數據的概率。
你可以理解為:
模型假設有某個參數 → 在這個參數下,生成我們現在手上這批數據的 “可能性” 有多大。
📊 和概率的區別?
很多人容易混:
✅ 概率:已知參數,算事件的可能性。
✅ 似然:已知數據,換著看哪個參數下更可能生成這些數據。
雖然數學公式長得一樣,但用途反過來。
🏗️ 數學表達
假設:
-
數據:
-
參數:
概率:
似然函數:
區別在於:
-
概率: 固定,看 。
-
似然: 固定,看 。
🌟** 舉個簡單例子 **
假設你有一個硬幣,拋了 10 次,結果有 7 次正面。
我們想估計硬幣正面的概率 。
✅ 模型假設
拋硬幣次數:10
正面概率:
事件:7 次正面
✅ 似然函數
其中:
-
是組合數(固定值,不影響最大化)。
-
核心是:
給定 ,生成這個數據(7 正 3 反)的概率。
🌟 最大似然估計(MLE)
通常我們用:
找到讓似然函數最大的參數。
在這個例子裡:
-
最大化
-
最優
這就是最大似然估計。
🔥 在機器學習中
機器學習裡的很多訓練,其實都是:
用最大似然,去擬合參數。
比如:
-
回歸模型 → 高斯分佈的最大似然
-
分類模型 → softmax 下的最大似然
-
神經網絡 → 交叉熵損失,其實就是最大似然推導出來的
✅ 總結一句話
似然函數 = 在給定參數下,觀察到當前數據的概率(把它看作參數的函數)。
最大化似然,就是找最可能生成數據的參數。
8、感知機#
感知機(Perceptron) 是一種非常基礎的 二分類線性模型,可以看作是神經網絡的最早形式(單層、無隱藏層)。
它的主要特點和要點是:
✅ 基本形式:
感知機就是用一個權重向量 w 和偏置 b,去對輸入特徵向量 x 做線性組合,再經過一個符號函數(sign function),決定輸出是 +1 還是 -1。
公式:
f(x) = sign(w·x + b)
✅ 目標:
找到一組 w, b,讓所有樣本能被一個超平面分開(即線性可分)。
✅ 訓練算法:
-
初始化權重和偏置(通常為零或小值)。
-
對每個誤分類樣本,更新權重:
w ← w + η * y * x
b ← b + η * y
這裡 η 是學習率,y 是真實標籤(+1 或 -1)。 -
持續迭代,直到所有樣本都被正確分類(或達到最大迭代次數)。
✅ 局限性(硬傷):
-
只能處理線性可分的問題。非線性數據(比如 XOR 問題)它完全無能為力。
-
沒有概率輸出,只是硬分類。
-
容易受噪聲和異常值影響。
✅ 意義和歷史地位:
雖然現在深度學習早已超越感知機,但感知機是神經網絡發展的起點。當年 Minsky 和 Papert 在 1969 年寫的《Perceptrons》一書指出它不能解決 XOR 問題,這直接導致 AI 寒冬的到來。直到多層網絡(MLP)和反向傳播算法出現,才打破了這個局限。
9、多層感知機#
二分類問題公式
二分類問題公式與多分類問題區別在於 的形狀為。(k 為類別個數)
每一層隱藏層都需要一個激活函數(非線性函數),如果沒有激活函數,就相當於一個大的線性函數。
輸出層可以不需要激活函數。
多層感知機多分類問題與 Softmax 回歸的區別,在於多了隱藏層,其餘均相同。
代碼
import torch
from torch import nn
# 構建模型,隱藏層包含256個隱藏單元,並使用了ReLU激活函數
net = nn.Sequential(nn.Flatten(),nn.Linear(784,256),nn.ReLU(),nn.Linear(256,10))
代碼解釋
nn.Sequential()
用來按順序堆疊一系列子模塊(層、激活函數等),自動組織前向傳播。換句話說:它就是一個有序容器,把你想要的網絡模塊按順序包起來。
nn.Flatten()
把輸入的多維張量展平成一維向量。
對 MNIST 來說,輸入圖像 shape 是 [batch_size, 1, 28, 28](灰度圖),Flatten 後變成 [batch_size, 784],方便接入全連接層。
10、激活函數#
Sigmoid 函數#
✅ Sigmoid 激活函數 定義為:
✅ 輸出範圍:
(0, 1) —— 把任意實數映射到 0 和 1 之間。
✅ 圖像特徵:
-
S 形曲線(所以叫 sigmoid,sigmoid = S 型)。
-
中心對稱於 (0, 0.5)。
-
當 很大或很小時,梯度接近 0 —— 梯度消失。
✅ 優點:
-
可以解釋為概率(尤其適合二分類最後一層)。
-
光滑、連續、可微。
✅ 缺點(致命的):
-
梯度消失:當 很大或很小時,導數接近 0,反向傳播幾乎沒法更新權重。
-
輸出不以 0 為中心:這會讓梯度更新時收斂變慢,因為正負梯度不對稱。
-
容易飽和:輸入太大或太小都會被壓平。
✅ 現在的主流做法:
-
除了二分類最後一層,隱藏層幾乎都不用 sigmoid,而是用 ReLU 或更先進的變體。
-
如果是二分類,最後一層用 sigmoid,損失函數用 binary cross entropy。
Tanh 函數#
✅ tanh(雙曲正切)激活函數 定義:
✅ 輸出範圍:
(-1, 1) —— 把任意實數壓縮到 -1 到 1 之間。
✅ 圖像特徵:
-
S 形曲線(跟 sigmoid 類似,但上下對稱)。
-
中心對稱於 (0, 0)(這比 sigmoid 好,輸出均值更接近 0,有助於優化收斂)。
-
當 很大或很小時,也會出現梯度消失。
✅ 優點:
-
相比 sigmoid,輸出以 0 為中心,對梯度更新更友好。
-
光滑、連續、可微。
✅ 缺點(核心問題):
- 和 sigmoid 一樣,容易飽和 → 梯度消失。
✅ 現在的主流做法:
-
在某些需要對稱輸出的模型(比如 RNN)中,tanh 仍然有用。
-
但對於深層神經網絡的隱藏層,現代主流還是用 ReLU 及其改進版。
ReLU 函數#
✅ ReLU(Rectified Linear Unit)激活函數 定義:
✅ 輸出範圍:
✅ 圖像特徵:
-
當 ,輸出 0。
-
當 ,輸出 。
簡單、直接,一條折線。
✅ 優點:
-
計算簡單,收斂快。相比於 Sigmoid 函數和 Tanh 函數,這倆均需要指數運行,指數運算偏貴。
-
不容易出現梯度消失(因為正區間梯度恆為 1)。
-
稀疏激活(很多神經元輸出 0,這有助於簡化模型)。
✅ 缺點:
-
死亡 ReLU 問題:如果某個神經元在訓練中被卡進負區間(輸出一直為 0),它可能再也更新不了(因為負區間梯度為 0)。
-
對輸入值不對稱(只保留正值)。
✅ 現代改進版:
-
Leaky ReLU:負區間給一個很小的斜率。
-
Parametric ReLU (PReLU):讓負區間的斜率可學習。
-
ELU、GELU、Swish:進一步改進。
11、模型複雜度#
12、正則化#
在機器學習中,正則化方法就是一種用來防止模型過擬合、提高模型泛化能力的技術集合。 它通過在模型的學習過程中引入一些 “懲罰” 或 “限制”,來約束模型的複雜度,使得模型不會過於 “完美” 地擬合訓練數據中的每一個細節(尤其是噪聲),從而能夠更好地適應新的、未見過的數據。
只在訓練中使用,預測時不需要。
正則化是什麼?
一句話:正則化通過在損失函數裡加入 “約束 / 懲罰”,主動限制模型自由度,逼它學到簡潔、泛化好的規律,而不是死記硬背訓練集裡的噪聲。
數學上常寫成
-
:原始經驗損失(交叉熵、MSE …)
-
:正則項(越大表示模型越 “複雜”)
-
:正則強度; 退化成無正則, 則把模型壓到極簡
核心作用
目標 | 解釋 |
---|---|
抑制過擬合 | 減小方差,提升對未見樣本的魯棒性 |
數值穩定 | 避免權重爆炸或矩陣奇異 |
可解釋性 | 稀疏化或結構化約束讓特徵 / 子網絡更易閱讀 |
防共線 | 在高維共線特徵下仍能給出唯一解 |
L1 正則化 (Lasso Regression)#
✅** 1. L1 正則化是什麼?**
一句話定義:
L1 正則化就是在損失函數中加入 所有權重絕對值的和 作為懲罰項。
它的目標是:
讓某些不重要的權重 自動變成 0,從而讓模型 “更簡單”。
✅ 2. 數學形式(簡潔地看)
普通損失函數是:
加上 L1 正則化後,變成:
其中:
-
是每個權重參數
-
是控制懲罰強度的超參數(越大,越 “狠”)
✅ 3. L1 的最大特點:讓部分權重變為 0(稀疏化)
這就是 L1 和 L2 的關鍵區別:
所以如果你希望模型自動挑出重要特徵、丟掉垃圾特徵,L1 正則是理想選擇。
✅ 4. 舉個例子(想像)
你在做一個房價預測任務,有 100 個輸入特徵,但實際上只有 5 個有用。
如果你用 L1 正則,訓練後模型可能只保留這 5 個特徵的權重,其余 95 個直接變成 0。
這相當於模型自動做了特徵選擇。
✅ 總結一句話
L1 正則化 = 懲罰權重絕對值 → 促使部分參數變 0 → 自動選擇重要特徵。
L2 正則化(權重衰退)(Ridge Regression)#
✅ 一句話定義
L2 正則化 是在損失函數中加入 模型參數平方的懲罰項,
用來抑制權重過大,避免過擬合。
✅ 數學形式
設模型的原始損失函數為:
加上 L2 正則後,變成:
其中:
-
:正則化強度(超參數)一般取
-
:所有權重的平方和(就是 L2 範數)
✅ 實際效果(直覺)
沒有 L2 的情況:
- 模型可能會瘋狂放大某個權重,導致對訓練數據擬合得很好,但泛化很差。
有了 L2 正則:
-
模型 “更保守”,不輕易拉大參數
-
模型對輸入的波動更穩定(泛化能力更強)
✅ 在 PyTorch 中怎麼用?
L2 正則 = 權重衰退,所以 PyTorch 中你只要在優化器裡加:
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, weight_decay=1e-4)
這個 weight_decay
參數其實就是 !默認就是 L2 正則。
✅ 在 sklearn 中怎麼用?
from sklearn.linear_model import Ridge # Ridge 就是帶 L2 的線性回歸
model = Ridge(alpha=1.0)
✅ 總結一句話
L2 正則化 = 懲罰權重平方,壓縮但保留全部參數,提升模型泛化能力。
Dropout (丟棄法)#
✅ 一句話定義
Dropout 是一種隨機屏蔽神經元的正則化方法,
它讓神經網絡在訓練時更 “健壯”,防止過擬合。
✅ 背後的動機
深度神經網絡容易過擬合,尤其當:
-
層數深
-
訓練數據小
-
參數多,模型複雜
原因是:
模型會學會 “依賴某些神經元組合” 來記住訓練數據 → 泛化能力變差。
Dropout 就是打破這種依賴 ——
訓練時,隨機屏蔽(置 0)一部分神經元,強迫網絡不能只靠一小撮神經元合作,而是必須具備冗餘能力。
✅ 怎麼操作的?
訓練時:
對某一隱層輸出向量
訓練期應用:
— 保留概率 $p$ 由你設定;通常 0.5–0.9。
-
:隨機掩碼,決定是否 “保留” 第 個單元。
-
:逐元素乘。
-
:inverted dropout 常用的重標定因子,使 ,保證訓練 / 推斷期激活尺度一致。
所以從數學角度看,它是一種 乘性二元噪聲注入,並非真的把神經元物理地刪掉。
隨機掩碼(random mask)= 一張用 0/1 取值填充的張量,決定在本輪前向 / 反向傳播裡哪些單元 “暫時關燈”,哪些 “正常工作”。
在 Dropout(或其它噪聲正則)中,它是把乘性噪聲注入網絡的最小 “開關矩陣”。
元素值
測試時:
不 Drop,全部激活,但輸出不縮放。
✅ 使用位置:
在全連接層(Dense Layer / Linear Layer)和其後的激活函數(如 ReLU, Sigmoid, Tanh 等)之間或之後。
- 更常見的是放在激活函數之後: Dense -> Activation -> Dropout
- 這樣做是因為 Dropout 的目的是隨機 “關閉” 神經元的輸出。激活函數的輸出代表了神經元的最終輸出信號,對其進行 Dropout 更直接地模擬了神經元的隨機失活。
- 少數情況也可能放在激活函數之前: Dense -> Dropout -> Activation
雖然不那麼主流,但也有研究和實踐表明這樣做有時也能取得效果。其邏輯是先對權重計算的線性組合結果進行 Dropout,然後再通過激活函數。
通常應用於一個或多個隱藏層 (Hidden Layers)。
- 對於較深的網絡,可以在多個隱藏層之後都使用 Dropout。
是否在所有隱藏層都使用,以及使用多大的 Dropout 率 (dropout rate/probability),通常是需要通過實驗調整的超參數。
一般不建議在輸出層 (Output Layer) 使用 Dropout。
- 輸出層負責產生最終的預測結果。如果在輸出層使用 Dropout,可能會干擾模型最終的預測輸出,特別是對於分類任務,可能會隨機丟棄掉某些類別的預測信號,這通常是不希望看到的。
✅ PyTorch 實現:
import torch.nn as nn
net = nn.Sequential(
nn.Linear(256, 128),
nn.ReLU(),
nn.Dropout(p=0.5), # Dropout 層:訓練時屏蔽 50% 神經元
nn.Linear(128, 10)
)
✅ Dropout 的優勢:
優點 | 描述 |
---|---|
抑制過擬合 | 強制網絡不能依賴某些特徵 |
增強泛化能力 | 每次訓練像在訓練不同 “子網絡” |
易用 | 一行代碼即可加入 |
⚠️ 注意事項:
-
Dropout 只在訓練階段起作用,測試階段必須關閉 Dropout。
-
如果模型已經很小或者數據量足夠,Dropout 有時反而會傷害性能(欠擬合)。
✅ 總結一句話:
Dropout 是一種 “訓練時隨機丟點,測試時全開” 的策略,增強模型魯棒性,防止過擬合。
Early Stopping (早停法)#
✅ 一句話定義
Early Stopping 是通過監控驗證集表現,在模型開始過擬合前停止訓練的一種方法。
🧠 為什麼需要 Early Stopping?
我們在訓練一個模型時,經常會看到這樣的現象:
輪數 | 訓練集準確率 | 驗證集準確率 |
---|---|---|
1 | 60% | 58% |
10 | 95% | 88% ✅ |
30 | 99% ✅ | 70% ❌ |
訓練集越來越好,但驗證集越來越差。這說明:
模型正在 “死記硬背” 訓練數據 → 過擬合開始了。
這時候繼續訓練反而是浪費時間、甚至破壞模型。
✅** Early Stopping 的核心思路 **
很簡單:
觀察驗證集的表現,一旦它開始下降,就立刻停止訓練,保留效果最好的模型。
這樣:
-
模型不會過擬合
-
訓練速度更快
-
一般不需要複雜的正則項
👉 所以 Early Stopping 是一種訓練級別的正則方法,而不是結構級別的。
✅ 總結一句話
Early Stopping 是最樸素但極有效的正則化策略:當驗證集不再變好,立刻停。
13、權重初始化#
為了避免 “信號爆炸或消失”,我們希望
網絡裡每一層的激活(正向)和梯度(反向)的均值 = 0,方差 = 常數。
具體地它把每層的輸出 和梯度 當作隨機變量:
方向 | 均值 | 方差 |
---|---|---|
正向 | ||
反向 |
其中 是兩個你自己定的常數(一般取 1 或 2),且 對所有層 和所有通道 都一樣。
推導一層的 “方差守恆” 條件(全連接層為例)
設
常見假設
-
-
-
與 獨立、元素之間近似獨立
於是
要求 輸出方差繼續等於 ⇒
反向同理(梯度一路乘 傳播),要求
兩端都顧及 → 折中方案
這就是 Glorot/Xavier 初始化。隨機初始化
Xavier 初始化(Glorot 初始化)#
一句話:它是一種設置網絡權重初值的策略,通過讓正向激活和反向梯度在每一層都保持方差相近,緩解深度網絡裡的梯度消失 / 爆炸問題。提出者是 Xavier Glorot 與 Yoshua Bengio(2010)。
具體公式
采樣分布 | 建議方差 | 實際采樣區間 / 標準差 |
---|---|---|
均勻 U (−r, r) | ||
正態 𝒩(0, σ²) | 同上 |
-
n_in
: 本層每個神經元接收的輸入維度 -
n_out
: 本層神經元個數
對 卷積層:
與 He 初始化的區別
名稱 | 建議方差 | 適用激活 |
---|---|---|
Xavier/Glorot | Sigmoid、tanh、soft-sign 等雙側激活 | |
He/Kaiming | ReLU、Leaky-ReLU、GELU(單側激活會丟掉 1/2 能量,需更大方差) |