在上一篇文章里,我们讨论了协同过滤(Collaborative Filtering)的核心思想——相似的人,喜欢相似的东西。

这听起来很优雅,也很符合直觉。但如果你真的去做一个推荐系统,很快就会发现:真正困难的地方,不在算法,而在细节。

不是用什么模型,而是数据怎么处理,新用户怎么办,训练怎么加速,结果怎么解释……这些问题,往往决定了系统是否真的可用。

今天这一篇,我们不再讲新的算法,而是讨论推荐系统是如何在工程上被真正实现出来的。

为什么很多推荐系统一开始都表现很奇怪?

这是一个非常真实的现象。

假设你刚注册一个 App,系统还不了解你。于是它开始推荐:

CODE
完全随机
完全不相关
完全看不懂

你可能会觉得,这个推荐系统是不是坏了?其实不是,是因为它没有数据。

假设有一个用户:

CODE
从未点击
从未购买
从未评分

在协同过滤模型里,这意味着没有行为数据。系统就会学出:

CODE
用户特征 = 0

于是预测结果就会变成: 

CODE
所有推荐 = 0

这显然不合理。

均值归一化(Mean Normalization)

这是推荐系统里最经典的一个工程技巧。它的目标非常简单:让新用户的初始推荐,至少是合理的。 

假设一个内容平台有三部剧:

CODE
剧A:平均评分 9.2
剧B:平均评分 8.5
剧C:平均评分 6.1

现在来了一个新用户,如果系统没有做任何处理,很可能预测:

CODE
评分 = 0

但如果做了均值归一化,系统会预测:

CODE
评分 ≈ 平均值

也就是:

CODE
剧A:9.2
剧B:8.5
剧C:6.1

这显然更合理。

原理其实非常简单,步骤只有三步: 

Step 1:计算每个物品的平均值 

CODE
μ = 平均评分

假设评分矩阵是:

CODE
        用户1 用户2 用户3
电影A     5     4     5
电影B     3     3     4
电影C     1     2     1

则计算每部电影的平均值如下:

CODE
电影A: (5+4+5)/3 = 4.67
电影B: (3+3+4)/3 = 3.33
电影C: (1+2+1)/3 = 1.33

Step 2:减去平均值 

CODE
新评分 = 原评分 - 平均值

我们把用户的评分减去平均值得到:

CODE
        用户1 用户2 用户3
电影A   +0.33  -0.67  +0.33
电影B   -0.33  -0.33  +0.67
电影C   -0.33  +0.67  -0.33

这样,现在模型看到的是相对偏好,而不是绝对评分。

Step 3:预测完成后再加回来 

CODE
最终预测 = 模型输出 + 平均值

假设模型预测:

CODE
新用户 对 电影A 的偏好 = 0

那么,

CODE
最终评分 = 0 + 4.67

也就是,预测评分是4.67,而不是0,这一步就是把“平均水平”加回来。

这样做有两个好处:1、训练更稳定,2、新用户预测更合理。特别是第二点,非常关键。 

因为没有评分的新用户,如果不做归一化,系统会默认预测所有评分为 0,这显然没有意义。

推荐系统真正的核心:优化(Optimization)

很多人以为推荐系统的核心是算法,但实际上,真正的核心是如何让模型不断变好,这就是优化(Optimization)。

一个非常直观的比喻,你在调整一个旋钮来预测误差,每次微调一点,直到误差最小,这就是梯度下降(Gradient Descent)。

为什么现在推荐系统几乎都用 TensorFlow / PyTorch?

因为它们有一个非常关键的能力:自动求导(Auto Diff)。

以前工程师需要手动计算导数,比如:

CODE
∂J / ∂w

这非常复杂。现在只需要:

CODE
with tf.GradientTape():
    loss = cost_function()

会自动计算梯度,然后更新参数。一个极简示例如下:

CODE
import tensorflow as tf
w = tf.Variable(3.0)
x = 1.0
y = 1.0
optimizer = tf.keras.optimizers.Adam(0.01)
for step in range(30):
    with tf.GradientTape() as tape:
        prediction = w * x
        loss = (prediction - y) ** 2
    grads = tape.gradient(loss, [w])
    optimizer.apply_gradients(
        zip(grads, [w])
    )

你只需要告诉系统,损失函数是什么,剩下的求导、更新、优化,全部自动完成。

找相似商品(Similar Items)

你一定见过:

CODE
看过这个商品的人还看了
猜你喜欢
类似商品

这背后的逻辑其实非常简单:找“特征最接近”的商品。

数学表达其实只有一句话——距离最小。

假设系统为每个商品学出一个向量:

CODE
商品A: [0.9, 0.1, 0.3]
商品B: [0.88, 0.12, 0.35]
商品C: [0.1, 0.9, 0.2]

系统会计算向量距离,如果距离很小,就认为很相似。

这正是推荐系统中寻找相关商品的一种标准方式:通过比较两个物品特征向量之间的距离,找到最相似的物品。

推荐系统并不是万能的

它有两个典型限制。

限制 1:冷启动问题(Cold Start) 

比如新用户、新商品,系统没有数据,就很难预测。 

限制 2:无法使用额外信息 

比如用户年龄、地理位置、设备类型、浏览器等,这些信息很有价值,但传统协同过滤不会用。

这也是为什么现代推荐系统往往会结合:

CODE
协同过滤
内容推荐
深度学习

而不是只用一种方法。

真正成熟的推荐系统,往往有三个层

这是一个非常典型的工业结构。

第一层:召回(Recall) 

目标:找出 1000 个候选商品 

第二层:排序(Ranking) 

目标:预测点击概率 

第三层:重排(Re-ranking) 

目标: 优化用户体验 

很多人第一次看到这三个层,会觉得它们只是“技术拆分”。但从工程角度看,它们其实是三个完全不同的决策: 

CODE
召回(Recall)   → 找谁
排序(Ranking)  → 谁更可能点击
重排(Re-ranking)→ 怎么展示更合理

你可以把它理解成: 先找一批候选人,再判断谁更合适,最后决定谁先上台。


在很多讨论中,我们很容易把推荐系统的成功归因于某一个算法,或者某一个模型的突破。但当系统真正上线、真正面对真实用户时,你会逐渐意识到:推荐系统的质量,往往不是由最复杂的模型决定的,而是由最基础的细节决定的。

是否做了合理的数据预处理,是否处理好了新用户的冷启动,是否保证了训练过程的稳定性,是否让结果在边界情况下依然可解释、可预期——这些看似微小的决定,会在系统运行的每一天不断累积,最终形成用户对产品的整体感受。

从某种意义上说,推荐系统并不是一次性的设计,而是一种持续演进的工程。它不会在某一个版本突然变得完美,而是在无数次迭代中,逐渐变得更可靠、更稳定,也更接近用户真实的需求。

而一个成熟的团队,往往也会在这个过程中形成一种共识:真正长期有效的系统,不是因为它足够复杂,而是因为它在每一个细节上都足够认真。

附录:本文涉及的专有名词中英文对照(标红为最核心)

中文
English
一句话说明
均值归一化
Mean Normalization
将数据减去平均值,使模型更稳定并改善新用户预测
冷启动
Cold Start
新用户或新商品缺乏数据导致推荐困难的问题
梯度下降
Gradient Descent
通过不断调整参数减少预测误差的优化方法
自动求导
Automatic Differentiation (Auto Diff)
系统自动计算梯度的技术
优化器
Optimizer
用于更新模型参数的算法
相似商品
Similar Items
与当前商品特征最接近的其他商品
特征向量
Feature Vector
用数字表示用户或商品特征的数组
距离
Distance
衡量两个向量是否相似的指标
召回
Recall
从大量数据中筛选出候选结果
排序
Ranking
对候选结果进行优先级排序