跳转到内容

SLIM — 让数据自己学一张稀疏的"看了又看"权重表

待复核

SLIM(Sparse Linear Methods)是一种把”用户买/看过什么”直接拟合成”下一个该推什么”的推荐方法。它学一张 n×n 的物品权重表 W,每一格 W[i][j] 表示”看过 i 的人有多大概率也想看 j”。

日常类比:电商首页那条”看了又看”的推荐栏背后那张表。传统做法是手写一个相似度公式(cosine、皮尔逊系数)现场算两两物品的相似度;SLIM 的思路是让数据自己学这张表——而且学出来是稀疏的,每一列只有几十到几百个非零项,正好能塞进一个 KV 存储里在线毫秒查。

写成数学:给定用户-物品交互矩阵 A(行是用户,列是物品,0/1 表示是否点过),SLIM 要找一个 W 让 A·W 尽量复刻 A 自身——也就是”用一个用户已经点过的物品,去预测他还会点什么”。预测分数向量就是 a_u·W,对未交互过的物品按分数排序取前 N 个推荐。

不理解 SLIM,下面这些事都解释不清:

  • 为什么 2019 年 EASE 论文还能用一个 30 行 numpy 把深度推荐模型打趴下——SLIM 是它的祖师爷
  • 为什么很多公司线上的”i2i 召回表”还是先离线训练、在线 KV 查表两步走——SLIM 给的就是这个工程形态
  • 为什么”线性 baseline”在推荐学术圈被反复证明强于复杂 deep model——SLIM/EASE 是踢馆王
  • 为什么 item-item 协同过滤(item-CF)从 2001 年到今天还没死
  • 为什么大多数 deep recsys 论文如果不报 SLIM/EASE 的对照实验会被审稿人怼——它们是 RecSys 圈内的”必跑 baseline”

SLIM 把推荐拆成 三件事

  1. 目标函数:min 0.5·||A − A·W||² + (β/2)·||W||² + λ·||W||₁。前一项要求 A·W 复刻 A(拟合好),中间是 L2 防过拟合,最后是 L1 把 W 大量项压成 0(稀疏)。这个 L1+L2 的组合叫 ElasticNet(2005 年 Zou-Hastie 提出)——类比:L1 像”超重就砍掉”,L2 像”整体压一压别太猛”,两者一拼既稀疏又稳。

  2. 两条硬约束:W ≥ 0(权重非负,方便解释——“看过 i 只会增加 j 的可能性、不会减少”);diag(W) = 0(自己不能用自己预测自己,否则 W 会退化成单位矩阵作弊,训练 loss 直接归零但泛化为零)。

  3. 按列独立求解:W 的每一列 w_j 是一个独立的 LASSO 子问题——把”预测 A 的第 j 列”拆出来单独优化,输入特征是 A 的其它列。用坐标下降每次只改一个权重;软阈值像”小于门槛的直接砍成 0”。列之间互不依赖 → n 万个物品就是 n 万个独立小问题,能塞进 Spark / Ray。

预测时给用户 u 一个分数向量 a_u·W,对没点过的物品按分数排序取 top-N。因为 W 稀疏 + a_u 稀疏,实际只要做”用户点过的几个物品 × 它们各自非零权重”的小计算,速度和 item-CF 一个量级,但准确率高一截。

案例 1:把 SLIM 当离线 i2i 召回表

Section titled “案例 1:把 SLIM 当离线 i2i 召回表”

中等规模电商或视频站(物品 1 万到 10 万、有点击日志),离线跑一次 SLIM 得到稀疏 W;把 W 的每一列前 K 大写进 Redis:key = item_i,value = [(item_j, weight)] × K。在线给用户取最近 N 条历史,对每条查一次表,加权合并出候选——这就是工业界最常见的”i2i 召回”形态。

伪代码骨架大概是这样:

# 离线(训练)
for j in range(n_items): # 每个物品独立
a_j = A[:, j] # 第 j 列:所有用户对 j 的交互
A_others = A.copy(); A_others[:, j] = 0 # 屏蔽 j 自己
w_j = elasticnet(A_others, a_j, l1=lam, l2=beta, nonneg=True)
save_topk_to_kv(j, w_j, K) # 写 Redis
# 在线(预测)
def recommend(user_history, top_n):
score = defaultdict(float)
for i in user_history:
for (j, w) in kv.get(i): # 查 K 个邻居
score[j] += w
return sorted(score, key=score.get)[:top_n]

SLIM 的好处是这张表是从数据学的,不是手算的 cosine 相似度——后者在长尾物品上经常翻车。

案例 2:当 baseline 戳穿”deep is all you need”

Section titled “案例 2:当 baseline 戳穿”deep is all you need””

Dacrema 等人 2019 年发现:18 篇 top 会议 deep 模型里,有 11 篇用对调的 SLIM/item-kNN/纯热门就能打过。实操三步:

# 1) 调库装好交互矩阵 A(用户×物品,0/1)
from implicit.nearest_neighbours import ItemItemRecommender # SLIM-like
model = ItemItemRecommender(K=100) # 每列最多 100 个非零
# 2) 设超参:lambda∈[1,10]、beta∈[1,5] 通常已接近最优
model.fit(A) # 离线学 W
# 3) 和新方法比 HR@10 / NDCG@10,半天出对比表
print(evaluate(model, test_set))

逐部分解释:先装数据 → 再拟合稀疏 W → 最后只看公开指标差距。过不了这关,就别急着上 GNN。

预测分数是 score(u, j) = Σᵢ a_u[i] · W[i][j]——一个线性求和。假例子:用户看过 {耳机, 手机壳},要解释为何推”充电宝”:

W[耳机][充电宝]=0.4, W[手机壳][充电宝]=0.1, W[水杯][充电宝]=0.0
→ 贡献排序:耳机 > 手机壳 → "因为你看过耳机,所以推荐充电宝"

把贡献最大的 3 到 5 个 i 抓出来即可。SLIM 强制非负,“为什么不推”就看贡献接近 0 的项。比深度模型的 attention map 好懂。

  1. W 不是相似度矩阵:很多人误以为 W[i][j] 是 i 和 j 的相似度。它不是。它是回归权重——“用 i 预测 j 的最优系数”。所以 W 通常非对称(W[i][j] ≠ W[j][i]),不能拿来做物品聚类,也不能直接和 cosine 相似度对比”哪个更准”——它们衡量的不是同一回事。

  2. diag(W) = 0 不能忘:忘了这条约束,模型会用 a_j 直接预测 a_j,得到完美拟合 W = I(单位矩阵),泛化为零。新手写代码时常 debug 半天才发现是这里漏了。常见症状:训练 loss 接近 0,验证集 HR 也接近 0——这种”两边一起掉”的特征几乎专属这个 bug。

  3. 非负约束 W ≥ 0 是经验性的:Steck 2019 的 EASE 去掉 L1 + 非负、只留 L2,得到闭式解:令 P = (AᵀA + λI)⁻¹,则 W_ij = −P_ij / P_ii(i≠j)、W_ii = 0(等价于 W = I − P·diag(1⊘diag(P)))。准确率常比 SLIM 高——但 W 不稀疏,存储随 n² 爆炸。SLIM 适合大库 + 在线低延迟,EASE 适合中小库 + 离线刷分。

  4. 大物品数会被设计矩阵卡死:n > 10⁵ 时,每列 LASSO 的设计矩阵 A 仍是 m×n 大矩阵,内存和 I/O 是瓶颈。工程做法是先用共现 top-K 截候选,再对每个 j 只在候选集里跑 ElasticNet,而不是真求 n² 个权重。这个截断本身会引入近似误差,但实践中影响很小(点积矩阵稀疏度本来就极高)。

  5. L1 vs L2 超参数耦合:lambda(L1 系数)控制稀疏度,beta(L2 系数)控制泛化。常见误区是只调 lambda——其实在小数据集上 beta 调 0 直接退化成 LASSO,过拟合严重;在大数据集上 lambda 太大会让 W 变 0 矩阵直接挂掉。两者要一起 grid search。

适用

  • 隐式反馈(点击、播放、加购、收藏) + top-N 推荐场景
  • 物品规模 1e3 到 1e5、需要”离线训练 + 在线 KV 查表”的工程形态
  • 需要可解释性的推荐位(“因为你看过 X 才推 Y”)
  • 任何深度推荐模型论文的 baseline 对照组
  • 数据周期短(每天重训也行)、需要快速试不同特征/采样策略的实验阶段

不适用

  • 显式评分预测(1-5 星)——SLIM 是为隐式反馈设计的,预测评分用 MF 或 SVD 系列更合适
  • 极大物品库(n > 10⁶,例如全网新闻、全量商品)——n² 量级直接劝退,要先做候选截断或换 embedding 方案
  • 强冷启动(新物品零交互)——SLIM 的 W 全靠交互数据学,没数据就没权重;要补 fSLIM 或两塔(youtube-two-tower-2019)这种带侧信息的方案
  • 序列推荐(Next-Item Prediction,强依赖顺序)——SLIM 是”袋”模型,不看顺序
  • 用户特征丰富、想做精排的场景——SLIM 是召回级别的工具,精排还是要 GBDT / DNN 等带特征交叉的模型
  • 1996 年:Tibshirani 提出 LASSO,把 L1 正则做稀疏特征选择带入主流;统计学家发现”加个绝对值惩罚就能让大部分系数变 0”是个奇迹。
  • 2001 年:Sarwar 等人发表 item-CF 论文,让”看了又看”的相似度计算成为推荐工业标配——但相似度公式是手挑的(cosine / Pearson),调参靠玄学。
  • 2005 年:Zou 和 Hastie 提出 ElasticNet,把 L1 和 L2 揉一起,既稀疏又稳,从此回归界多了一把瑞士军刀。
  • 2008 年前后:Netflix Prize 让矩阵分解(koren-mf-2009)封神,整个推荐圈以为”item-CF 时代结束了”。
  • 2011 年:Ning 和 Karypis 在 ICDM 把 ElasticNet + 非负 + diag=0 三件套拼起来,开创”直接学稀疏 i2i 矩阵”这条路线,证明线性方法不仅没死,还能反超 MF 系列。
  • 2019 年:Steck 的 EASE 去掉 L1 和非负,给出闭式解,以 30 行 numpy 在多个公开 benchmark 击败深度推荐模型,让线性方法第二次封神;同年 Dacrema 等人的复现研究系统揭穿很多 deep recsys 论文的”虚假进步”。

整条路线的精神是:当工程问题的核心结构是稀疏 + 线性时,不要硬上深度模型——这个教训过去十年在 IR、推荐、文本检索上反复出现。

  1. 能用线性回归解决的,先别上深度学习——SLIM 用一个 ElasticNet 就把 item-CF 升级了一个台阶;类似的反例还有 BM25 之于 salton-vsm-1975 文本检索
  2. 稀疏性不是副作用,是工程友好性——L1 让 W 在线查表毫秒级返回;密集的 EASE 准但难落地
  3. 把模型对齐工程形态,比堆架构更重要——SLIM 学的就是”i2i 召回表”这张工业界已有的表,迁移成本几乎为零
  4. 强 baseline 是学术健康的免疫系统——没有 SLIM/EASE 这种 baseline,整个 deep recsys 可能早就跑偏;学新方法时永远先确认它能打过线性 baseline
  5. 拆解 + 并行 = 工程友好——按列独立解的设计让 SLIM 天然适配 Spark / Ray / GPU,是 11 年前的论文里少见的”工业落地友好”姿态
  6. 约束的形式不是装饰——diag(W) = 0 这种”看起来无关紧要”的约束往往是模型不退化的关键;写论文和读论文都要警惕”约束去掉看会不会更准”这种伪问题
  7. 简单方法持续被低估——15 年前的 SLIM 在 2026 年仍能进 baseline 行列,说明工程界对”简单 + 强”的方法长期不够重视,喜欢追新不追稳
  • salton-vsm-1975 —— 向量空间模型,SLIM 用的”用户向量 × 物品向量”思路的祖师爷
  • okapi-bm25-1994 —— 文本检索版的”看了又看”,用 IDF 加权词项;SLIM 是推荐版,用学出来的 W 加权物品
  • simrank-2002 —— 另一种学物品相似度的图算法,靠迭代不靠回归
  • personalized-pagerank-2003 —— 在用户-物品二部图上做 PPR 是 SLIM 的图方法竞争对手
  • pagerank-1998 —— SLIM 是”每个物品对其它物品的影响力被回归学出来”,PageRank 是”每个网页对其它网页的影响力被随机游走算出来”
  • youtube-two-tower-2019 —— 现代两塔召回,处理 SLIM 不擅长的极大库 + 冷启动
  • ranknet-2005 / lambdarank-2006 —— 排序学习的早期代表,和 SLIM 同样属于”用机器学习取代手挑公式”的浪潮
  • bpr-2009 —— BPR — 用『i 比 j 更受欢迎』替代『i 是正例 j 是负例』