矩陣分解#
矩陣分解(Matrix Factorization,MF)是協同過濾的進階方法。
它把「用戶 - 物品」評分矩陣分解成兩個低維的隱因子矩陣,藉此緩解資料稀疏性,並保留 CF 的核心思想。
MF 是後續所有 embedding-based 方法的源頭:第 7 章的雙塔模型本質就是 MF 的神經網路版本。學會 MF 等於拿到深度推薦模型的入場券。
基本原理#
分解目標#
把 m × n 的用戶 - 物品評分矩陣 R 分解為兩個低維矩陣:
R(m×n) ≈ P(m×k) × Q(k×n)^TP:用戶隱因子矩陣,每位用戶對應一條 k 維向量Q:物品隱因子矩陣,每件物品對應一條 k 維向量k:隱因子數量(通常 10~200)
預測評分#
r̂(u,i) = p_u · q_i = Σ(p_uk × q_ik)隱因子可理解為潛在的「主題」或「特徵」,例如電影的動作程度、愛情程度。但這些維度是模型自己學出來的,不一定能語意解釋。
概念澄清:推薦系統說的「SVD」其實不是線性代數教科書的 SVD(R = UΣV^T,要求矩陣完整)。Simon Funk 在 Netflix Prize 提出的 Funk SVD 是用 SGD 直接學 P、Q,只在已知元素上算誤差。後續所有「SVD/SVD++」變體都沿用這個套路,不要被名字誤導。
為什麼低秩分解有效(幾何直覺)#
評分矩陣看似活在 n 維(物品數)的高維空間,但真實的用戶偏好其實由少數幾個潛在維度主導——多數人的口味可以用「動作 vs 文藝」「主流 vs 小眾」這類少量軸線大致描述。
低秩分解就是降維:把高維、稀疏、充滿空白的評分矩陣,投影到
k維的稠密隱因子空間。線性代數的 SVD 是用前k個奇異值做最佳低秩逼近;MF 則是「只在觀測到的格子上」做這件事,避免把空白硬擬合成 0 而帶偏向量。兩者產出的 user / item 向量都落在同一個可比較的空間,這正是後面所有「算內積找相似」的基礎。
隱因子數
k怎麼選:k小(1050)訓練快、記憶體省,適合召回層的海量用戶物品;200)能刻畫更細的偏好,適合排序層或品項較少的目錄。實務上交叉驗證,多數資料集在k大(100k≈128後損失就改善有限。
SVD 及其變體#
基礎 SVD(Funk SVD)#
損失函數:
L = Σ(r_ui - p_u · q_i)² + λ(||p_u||² + ||q_i||²)- 第一項:預測誤差(只在已知評分上累加)
- 第二項:L2 正則化,防止過擬合
SVD++#
加入隱式回饋(用戶看過什麼,即使沒評分也是訊號):
r̂(u,i) = μ + b_u + b_i + q_i · (p_u + |N(u)|^(-1/2) Σ y_j)μ:全域平均評分b_u、b_i:用戶 / 物品的偏差項(修正打分習慣)N(u):用戶 u 有過行為的物品集合y_j:物品 j 的隱式特徵向量
SVD++ 把「用戶看過什麼」與「用戶評過什麼」一併納入。Netflix Prize 冠軍方案的核心元件。
TimeSVD++#
把時間因素納入:用戶偏好隨時間漂移、物品熱度也會變化。
r̂(u,i,t) = μ + b_u(t) + b_i(t) + q_i · p_u(t)優化演算法#
隨機梯度下降(SGD)#
for epoch in range(num_epochs):
for u, i, r in training_data:
error = r - predict(u, i)
P[u] += lr * (error * Q[i] - reg * P[u])
Q[i] += lr * (error * P[u] - reg * Q[i])交替最小二乘(ALS)#
ALS 在 Spark MLlib 是內建演算法,業界處理大規模資料的主流選擇。
核心思想:交替固定一個矩陣,求另一個的解析解。
1. 初始化 Q 為隨機矩陣
2. 固定 Q,解析求解 P = R × Q × (Q^T × Q + λI)^(-1)
3. 固定 P,解析求解 Q
4. 重複 2-3 直到收斂每一步都有解析解、計算穩定,且每位用戶 / 物品的更新互不依賴,天然可平行化,適合處理大規模稀疏矩陣。
SGD vs ALS#
| 維度 | SGD | ALS |
|---|---|---|
| 收斂速度 | 較慢 | 較快(稀疏資料) |
| 平行化 | 困難 | 容易 |
| 實作複雜度 | 簡單 | 中等 |
| 適用場景 | 顯式評分 | 隱式回饋 |
隱式回饋處理#
One-Class 問題#
實際推薦系統中,明確評分很少,更多的是隱式回饋(點擊、瀏覽、購買)。
| 回饋類型 | 特點 | 範例 |
|---|---|---|
| 顯式回饋 | 用戶明確表達喜好 | 評分、按讚 |
| 隱式回饋 | 只有正樣本,缺少負樣本 | 點擊、購買 |
Weighted-ALS#
針對隱式回饋的改進:把所有「沒互動」當成弱訊號的負樣本,但給予低權重。
L = Σ c_ui × (r_ui - p_u · q_i)² + λ(||P||² + ||Q||²)r_ui = 1(有行為)或0(無行為)c_ui = 1 + α × 行為次數(信心度)
行為次數越多,信心度越高。Hu/Koren/Volinsky 原論文預設 α = 40。
負樣本採樣#
由於沒有明確負樣本,需要構造:
| 方法 | 說明 | 效果 |
|---|---|---|
| 隨機採樣 | 均勻隨機選無行為物品 | 一般 |
| 熱門採樣 | 按物品熱度採樣 | 較好 |
| 曝光未點擊 | 從曝光但未點擊中採樣 | 最好 |
熱門採樣的邏輯:熱門物品用戶更可能知道其存在,如果還沒互動,更可能是真正的負樣本。
BPR:面向排序的矩陣分解#
動機#
傳統 MF 優化「評分絕對值的準確度」,但推薦的最終目標是排序正確。BPR(Bayesian Personalized Ranking)直接優化排序。
BPR 優化目標#
max Σ log σ(r̂_uij) - λ||Θ||²r̂_uij = r̂_ui - r̂_uj(u, i, j):用戶 u 對物品 i 有正回饋,對物品 j 無回饋σ:sigmoid 函數
BPR 直接優化「正樣本排在負樣本前面」的機率,與最終排序目標一致。
Weighted-ALS vs BPR#
| 維度 | Weighted-ALS | BPR |
|---|---|---|
| 任務型態 | 回歸(0/1 + 信心度) | 排序(pairwise) |
| 評估指標 | RMSE(不太相關) | AUC(與優化目標一致) |
| 訓練輸入 | (u, i, r) | (u, i, j) 三元組 |
| 業界使用 | 召回層 | 召回層、排序層 |
BPR 訓練流程#
for epoch in range(num_epochs):
for u, i, j in sample_triplets():
x_uij = predict(u, i) - predict(u, j)
gradient = sigmoid(-x_uij)
update_parameters(u, i, j, gradient)評估指標:AUC#
AUC = Σ I(r̂_ui > r̂_uj) / (|positive| × |negative|)AUC 衡量「正樣本排在負樣本前面」的機率,是隱式回饋場景的主要排序指標。
向量檢索(ANN)#
這是現代召回層的核心:MF 訓練完得到 user / item 向量後,線上召回就退化成「給一條 user 向量,從千萬條 item 向量裡找 TopK 最近鄰」。靠 ANN(Approximate Nearest Neighbor)做毫秒級回應。
主流工具#
| 工具 | 特點 | 適用場景 |
|---|---|---|
| Faiss | Meta 開源,CPU/GPU 均支援 | 通用、大規模 |
| HNSW | 圖索引,召回率與速度兼顧 | 工業界主流 |
| Annoy | Spotify 開源,輕量、樹索引 | 中小規模 |
| ScaNN | Google 開源,極致速度 | 超大規模 |
Faiss 範例
import faiss
# 建立索引
d = 128 # 向量維度
index = faiss.IndexFlatL2(d) # 暴力 L2 距離(小規模可用)
index.add(item_vectors)
# 查詢最相似的 k 個物品
k = 10
D, I = index.search(user_vector, k)正式環境通常會用 IndexHNSWFlat 或 IndexIVFPQ 換取速度。
聚類加速#
另一種思路:先對物品向量聚類,再分階段檢索。
1. 對物品向量做 K-means 聚類
2. 算用戶向量與每個聚類中心的相似度
3. 選相似度最高的若干群
4. 在選中的群內找具體物品優點:可順便控制推薦多樣性(從不同群各取一些)。
工程實踐#
模型訓練工具#
| 工具 | 語言 | 特點 |
|---|---|---|
| Spark ALS | Scala | 分散式,適合超大規模 |
| Implicit | Python | 專注隱式回饋,CPU 高效 |
| LightFM | Python | 支援混合特徵(user/item side info) |
模型更新策略#
| 策略 | 頻率 | 優點 | 缺點 |
|---|---|---|---|
| 全量更新 | 每天 | 準確 | 計算量大 |
| 增量更新 | 每小時 | 即時 | 可能累積誤差 |
| 混合策略 | - | 平衡 | 實作複雜 |
增量更新為何會累積誤差:只用新互動更新時,舊向量會逐漸偏離原本的全域最佳低秩逼近,誤差一輪輪疊加。定期全量重訓相當於把所有向量重新錨定回全域最佳;常見折衷是「每週全量 + 每日增量」,兼顧新鮮度與穩定度。
MF 的冷啟動對策#
MF 靠互動歷史學向量,新用戶 / 新物品沒有歷史就學不到向量:
| 情況 | 對策 |
|---|---|
| 新用戶 | 用人口統計、註冊情境初始化向量;或先走內容式推薦過渡 |
| 新物品 | 用標題、類別、標籤等元資料當代理向量;或用內容相似度引導 |
| 通用 | 訓練一個「從側資訊預測 MF 向量」的模型,補上缺失的 embedding |
這正是雙塔模型(第 7 章)的優勢:它直接以特徵為輸入,新用戶 / 新物品只要有特徵就能算出向量,天然緩解冷啟動。
從 MF 到雙塔#
本章開頭說「MF 是深度推薦模型的入場券」,銜接點在這裡:
| 維度 | 矩陣分解 | 雙塔模型 |
|---|---|---|
| 向量怎麼來 | 直接分解互動矩陣學 ID embedding | 用戶 / 物品特徵各過一座神經網路編碼器 |
| 能用的訊號 | 只有互動 | 互動 + 任意側資訊(屬性、上下文、文本) |
| 損失函數 | RMSE / BPR | 交叉熵、對比學習(in-batch negatives) |
兩者最終都把用戶與物品映到同一個向量空間、用內積算相似度——所以 MF 學會的「向量召回」直覺,可以原封不動搬到雙塔。
總結#
| 要點 | 說明 |
|---|---|
| 核心思想 | 把用戶和物品映射到低維隱因子空間 |
| Funk SVD | 用 SGD 學 P、Q,只在已知元素上算誤差 |
| SVD++ | 加入隱式回饋與偏差項 |
| ALS | 交替優化,天然平行化,適合大規模 |
| Weighted-ALS | 處理隱式回饋的回歸版本 |
| BPR | 排序學習版本,直接優化 AUC |
| 向量檢索 | 訓練完用 ANN(Faiss / HNSW)做線上召回 |
| 演進方向 | ➡️ 雙塔模型(MF 的神經網路版本) |
下一章:Bandit 演算法