想象你是一位掌管满汉全席的品鉴主厨。晚市即将开始,后厨采购团队从菜市场数以万计的食材中,凭“看着新鲜、品类大致对口”这类粗标准,匆匆挑出几十道候选菜端到你的案前。他们没时间细看,只能先快速筛掉明显不行的——这就是召回。而真正决定今晚菜单顺序的,是你这位品鉴主厨:你要亲自逐一尝过每道候选菜,从色、香、味、形、温度、摆盘、与酒的搭配等多个维度细细打分,排出“硬菜在前、时令小菜殿后”的上菜顺序,还要忍痛淘汰分数不达标的菜品。在推荐系统里,这个角色叫精排模型——它接收召回送来的几十到几百个候选,用精细的特征和模型逐条打分,决定用户最终看到什么、先看到什么、看不到什么。这篇文章,我们就跟着这位主厨走进后厨,见识它的“品鉴档案”(特征工程)、“搭味心法”(DeepFM)、“看人下菜”(DIN)、“品鉴笔记”(训练样本构建)和“高峰限时出菜”(在线推理优化),彻底搞懂这个推荐系统中真正的最终决策者。
精通精排模型,是你从“会调推荐接口”进阶为“能设计排序系统”的分水岭。信息流、广告、电商这些高价值场景中,精排直接决定了用户体验与平台收入的每一分钱。
目录
可视化演示
展示推荐系统精排阶段从召回候选到排序输出的完整流程,对比DeepFM与DIN模型架构差异,交互调节候选数量感受排序精度变化。
本节配有交互动效(进入视口后自动加载,约 3KB)。
精排排序流程 召回阶段从百万级物料中筛出数百候选,交由精排模型打分 精排模型对每个候选计算CTR预估分数,按分数降序排列 score = f(user, item, context) 精排是决定最终展示顺序的核心模块
精排模型定位:最终决策者
采购团队(召回)决定“候选名单”,品鉴主厨(精排)决定“上菜顺序与哪道被淘汰”。没有主厨的把关,再丰盛的食材也成不了一桌好席;同理,没有精排的精细打分,再优秀的召回也无法直接变成用户满意的体验。
在推荐系统里,精排模型就是这个“最终拍板的人”。它接收召回阶段送来的几十到几百个候选物品,利用丰富的用户、物品、上下文特征逐一对候选打分,按分数从高到低排出最终展示顺序,并根据分数阈值做截断,决定哪些候选能真正出现在用户面前。与召回“广撒网”不同,精排追求的是“极致精准”。
精排的典型场景包括信息流推荐(今日头条、抖音的“猜你喜欢”)、广告竞价排序(电商广告中 eCPM = pCTR × pCVR × 出价)、电商搜索结果重排等。这类场景候选数量不大,但对排序精度、业务指标(点击率、转化率、用户时长)的影响极其直接,因此是推荐系统中精度要求最高的模块。业界常用的精排模型包括 LR、GBDT、DeepFM、DIN 等,本文重点讲解后两者。
精排与召回的分工就像主厨与采购:采购管“广”,用轻量规则快速圈定范围;主厨管“精”,用全套品鉴手段在有限候选里排出最优顺序。理解了这层定位,后面的所有技术细节就都有了归宿。
- 特征输入拼接把用户特征、物品特征、上下文特征拼接成一个向量,作为模型输入。
- 打分函数f(·) 是精排模型主体(如 DeepFM、DIN),输出一个实数值分数,分数越高代表越值得展示。
- Sigmoid 映射σ 将分数压缩到 0 到 1 之间,作为点击概率 pCTR,供排序使用。
特征工程:主厨的品鉴档案
主厨不会凭一句“这道菜还不错”就做决定。他的案头有一本品鉴档案,记录每道菜的咸度、辣度、温度、摆盘精细度、上菜耗时、食材产地,甚至连今晚老主顾上次点过什么菜都写得清清楚楚。打分之前,先建档。
精排的“精”首先体现在特征上。特征工程是把原始数据变成模型能理解的输入,通常分为三类:用户特征(年龄、性别、历史点击/购买序列)、物品特征(类目、价格、质量分、品牌)、上下文特征(时间、地点、设备、当前场景)。此外还有用户与物品的交叉特征,例如“年轻男性 × 电竞类目”。
从技术角度看,离散特征(物品 ID、类目 ID)是稀疏的高维类别,一般先做 one-hot 编码,再经过 embedding 层映射成稠密向量;连续特征(价格、时长)则做均值归一化,消除量纲差异,帮助模型稳定训练。特征质量直接决定精排模型的上限——同样的模型结构,特征差一档,线上效果可能差一个量级。
档案记得越细,打分越有依据。但主厨很快发现,档案里任何一项都不能单独决定一道菜的命运——真正的功力在于看出各项之间的“默契”。这就要引出下一位主角:DeepFM。
- 均值标准化x 为原始连续特征,μ 为均值,σ 为标准差,把量纲不同的特征压到同一尺度,避免大数值特征主导梯度更新。
- One-hot 编码把物品 ID 这类稀疏离散 id 展开成只有一位为 1 的向量,维度等于 id 总数。
- Embedding 映射用矩阵 W_emb 把稀疏的 one-hot 投影成稠密的低维向量 e_id,既降维又表达语义相似性——相似的物品在向量空间里距离更近。
DeepFM模型:会搭味的主厨
名厨的独门功夫不在单维度打分,而在“搭味”。单独看一道菜,咸度适中、甜度略高,都没问题;但一口下去,咸甜交织之间竟能迸发出奇妙的层次感。这种“咸甜搭配”的口味,任何单一指标都描述不出来,必须看特征之间的交叉。
逻辑回归等线性模型只能对特征做加权求和,无法表达“咸度 × 甜度”这类二阶交叉。传统做法是人工构造交叉特征,但特征组合爆炸、覆盖不全。2017 年华为提出的 DeepFM,把因子分解机(FM)与深度神经网络并行融合,让模型自动学习二阶及高阶特征交叉,无需人工特征工程。
DeepFM 包含三条通路:一阶线性项保留每个特征本身的重要性;FM 项用隐向量内积为每一对特征组合建模二阶交叉;Deep 部分是一个多层感知机,捕获高阶非线性模式。三路共享 embedding 输入,输出相加后过 Sigmoid 得到点击概率,端到端联合训练。
有了会搭味的主厨,菜单品质立刻上了一个台阶。但这位主厨还有更绝的一手——他会“看人下菜”,记住不同食客的偏好,给四川客人多关注辣度,给广东客人多关注鲜甜。这招,叫做 DIN。
- 一阶线性项w0 为全局偏置,w_i·x_i 表示每个特征单独的贡献,相当于逻辑回归部分。
- 二阶交叉项⟨v_i, v_j⟩ 表示两个特征隐向量的内积,x_i x_j 表示两特征同时出现,二者相乘即为该特征组合对结果的二阶贡献。
- 深度网络项MLP(x) 把全部特征向量喂入多层感知机,自动捕获更高阶的非线性特征组合。
- Sigmoid 输出σ 把三路相加的结果压到 0~1 之间,得到点击率预估值。
DIN模型:看人下菜的主厨
真正的大师善于“看人下菜”。老主顾是四川人,上次来一口气点了三盘辣菜,那么今晚给他上水煮鱼时,主厨会格外关注“辣度”这个维度;而给一位广东客人上白灼虾,则更在意“鲜甜”与“火候”。同一道菜,面对不同客人,评价的侧重点完全不同。
用户的历史行为(点击、购买、收藏)是理解兴趣的金矿。早期模型简单地把用户所有历史行为 embedding 求平均——这等于把“爱吃辣”和“爱吃甜”的兴趣混在一起彼此稀释,任何一面都表达不清楚。2018 年阿里提出的 DIN(Deep Interest Network,深度兴趣网络) 解决了这个问题。
DIN 引入注意力机制:针对当前待打分的候选物品,用一个注意力网络为用户的每个历史行为计算一个权重,相关度越高权重越大,再对历史行为加权求和,得到“与当前候选强相关”的用户兴趣表达向量,拼接进精排模型一起打分。
主厨既懂搭味又懂看人,菜单自然越来越合食客胃口。不过,这套功夫不是天生的——主厨靠的是厚厚一本品鉴笔记,也就是训练样本。
- 历史行为向量v_i 是用户第 i 个历史行为(如上次点击的商品)的 embedding。
- 候选物品向量v_target 是当前待打分商品的 embedding,相当于主厨此刻“关心的焦点”。
- 注意力打分g(v_i, v_target) 是一个小网络,衡量某个历史行为与当前候选的相关程度。
- Softmax 归一化对 m 个打分做 softmax,得到和为 1 的注意力权重 a_i。
- 加权求和把所有历史行为按注意力权重相加,得到与当前候选强相关的用户兴趣表达。
训练样本构建:品鉴笔记
主厨的品鉴功夫来自日复一日的笔记:哪道菜客人动了几筷、哪道被退了回来、哪道被夸了又加点。但笔记有一个致命缺陷——它只记录“端上桌的菜”,那些从未上桌的菜,客人到底爱不爱吃,永远是个谜。
精排训练同样面临这种“选择性偏差”:线上日志只有曝光样本才有反馈。如果把曝光未点击当负样本、曝光点击当正样本,模型只会学会“在曝光过的物品里排序”,对没曝光的好物品一无所知,甚至放大热门偏差——这就是著名的曝光偏差。
常用解法是:正样本取曝光且点击的样本;负样本采用“曝光未点击 + 全量随机采样”混合,模拟真实的全量竞争环境;再按时间窗口切分训练集与测试集,只允许使用曝光时刻之前已经产生的特征,防止数据泄漏;最后用二分类交叉熵损失训练,优化 AUC 等排序指标。
笔记记好了,功夫练成了,真正的考验就来了:晚市高峰,100 桌客人在等,主厨必须在弹指之间完成品鉴。精排模型在线上的“极限出菜”,就是最后一块拼图。
- 真实标签y_i 取值为 0 或 1,1 表示点击(正样本),0 表示未点击(负样本)。
- 预测概率ŷ_i 为模型对第 i 个样本输出的点击概率。
- 交叉熵项y·log(ŷ) + (1−y)·log(1−ŷ) 衡量预测与真实标签的差距,正确类别的对数概率越大,损失越小。
- 批量平均对 N 个样本求和取平均,得到最终损失值,可通过梯度下降最小化。
在线推理优化:晚市高峰
晚市高峰,后厨兵荒马乱,一百桌客人在等。主厨不可能像备菜时那样每道菜品上十分钟——他必须在几秒钟内对几十道候选菜快速完成判断,还得保证品质不缩水。
线上精排同样要面对延迟预算:一次推荐请求往往要求在 50 毫秒内返回结果,超过预算用户就会明显感觉卡顿,甚至直接流失。模型越大、特征越多,推理越慢,因此工程上必须想尽办法把每一毫秒都榨出价值。
常用优化手段包括:INT8 量化把浮点权重压缩为 8 位整数,推理速度提升数倍而精度损失很小;特征缓存把高频的用户、物品特征提前算好放内存,避免重复计算;粗排 + 精排级联先用轻量模型粗筛一遍,再让精排精打,控制进入精排的候选数量;配合模型剪枝、批处理与并发推理提升整体吞吐。
从采购挑菜(召回)、建立品鉴档案(特征工程)、学会搭味(DeepFM)、看人下菜(DIN)、积累笔记(训练样本),再到高峰限时出菜(推理优化)——精排这位品鉴主厨的完整功夫,至此全部展现在你眼前。下次刷到一条“猜你喜欢”的信息流时,不妨想想:屏幕上的每一顺序,都是这位主厨在几十毫秒内替你精心拍板的。
- 缩放因子s 是浮点转整数的步长,由权重分布范围决定,把连续数值映射到整数刻度。
- 零点z 是浮点 0 对应的整数偏移,保证 0 能被无损表示。
- 量化取整⌊x/s⌋ 向下取整,得到整数表示,乘法运算变成更快的整数运算。
- 范围裁剪clamp 把数值限制在 0~255 的 8 位整数范围内,防止溢出。
AI 应用场景
场景一:信息流“猜你喜欢”的精排大厨
阶段:精排与排序阶段
解决的问题:召回阶段从千万级内容中筛出几百条候选,但候选之间差异细微,人工规则根本无法精细排序。精排用 DeepFM 建模特征交叉、DIN 建模用户实时兴趣,逐条打分决定展示顺序。
具体案例:某资讯 App 将精排模型升级为 DeepFM + DIN 后,线上点击率提升 6%,人均阅读时长提升 8%。工程上通过 INT8 量化把模型压到原体积的四分之一,单次请求延迟稳定在 40ms 以内,完美满足“晚市高峰出菜”的时限。
场景二:广告竞价排序的“毫秒级裁判”
阶段:广告精排与竞价阶段
解决的问题:广告排序不能只看点击率,还得结合转化率与广告主出价。精排需要同时预估 pCTR 与 pCVR,计算出 eCPM = pCTR × pCVR × 出价 参与竞价排序,对实时性要求比信息流更苛刻。
具体案例:某广告平台在精排中引入 DIN 感知用户对广告创意的兴趣,并用特征缓存 + 量化将排序模块延迟从 80ms 降至 30ms,广告收入提升 12%。主厨不仅更懂食客口味,出菜速度也更快了。
Python 演示
import numpy as np def sigmoid(x): return 1.0 / (1.0 + np.exp(-x)) class DeepFM: def __init__(self, n_feat, k=8, dnn_units=(32, 16), lr=0.05): self.k = k self.lr = lr self.w0 = 0.0 rng = np.random.default_rng(42) self.w = rng.normal(0, 0.01, n_feat) self.V = rng.normal(0, 0.01, (n_feat, k)) sizes = [n_feat] + list(dnn_units) + [1] self.weights = [] self.biases = [] for i in range(len(sizes) - 1): self.weights.append(rng.normal(0, 0.05, (sizes[i], sizes[i + 1]))) self.biases.append(np.zeros(sizes[i + 1])) def forward(self, X): # 一阶线性项:每个特征的独立贡献 linear = self.w0 + X @ self.w # FM 二阶交叉项的等效快速计算 xv = X @ self.V inter = 0.5 * (np.sum(xv * xv, axis=1) - np.sum(np.square(X) @ np.square(self.V), axis=1)) # 深度网络项:捕获高阶非线性组合 deep = X for W, b in zip(self.weights, self.biases): deep = np.tanh(deep @ W + b) return sigmoid(linear + inter + deep.ravel()) def main(): rng = np.random.default_rng(0) n, n_feat = 2000, 20 # 构造高稀疏的 0/1 特征,模拟 one-hot 后的推荐场景 X = (rng.random((n, n_feat)) > 0.7).astype(np.float64) y = (X[:, 0] + X[:, 3] + X[:, 7] * X[:, 11] > 0.4).astype(np.float64) model = DeepFM(n_feat, k=8, lr=0.05) for epoch in range(30): pred = model.forward(X) eps = 1e-12 # 二分类交叉熵损失 loss = -np.mean(y * np.log(pred + eps) + (1 - y) * np.log(1 - pred + eps)) m = int(y.sum()) order = np.argsort(pred) rank = np.empty(n, dtype=int) rank[order] = np.arange(n) # 用秩和法计算 AUC,衡量排序质量 auc = (rank[y == 1].sum() - m * (m + 1) / 2.0) / (m * (n - m)) if epoch % 5 == 0: print("epoch {:02d} loss {:.4f} auc {:.4f}".format(epoch, loss, auc)) if __name__ == "__main__": main()
X = (rng.random((n, n_feat)) > 0.7).astype(np.float64)生成高稀疏的 0/1 特征,模拟真实推荐场景里 one-hot 之后的稀疏输入linear = self.w0 + X @ self.w一阶线性项:每个特征独立贡献的加权和,相当于逻辑回归部分xv = X @ self.V特征矩阵与隐向量矩阵相乘,得到每个样本、每个隐维度的表征inter = 0.5 * (np.sum(xv * xv, axis=1) - np.sum(np.square(X) @ np.square(self.V), axis=1))FM 二阶交叉的等效公式,把 O(n²) 的两两内积化成 O(nk),大幅加速计算deep = np.tanh(deep @ W + b)深度网络逐层做非线性变换,捕获高阶特征组合return sigmoid(linear + inter + deep.ravel())三路相加后过 Sigmoid,输出点击概率loss = -np.mean(y * np.log(pred + eps) + (1 - y) * np.log(1 - pred + eps))二分类交叉熵损失,对应正文中的 BCE 公式auc = (rank[y == 1].sum() - m * (m + 1) / 2.0) / (m * (n - m))用秩和法高效计算 AUC,衡量“正样本排在负样本前面”的概率if __name__ == "__main__":声明程序入口,保证脚本可直接运行
常见误区
误区“我一直以为精排就是把召回送来的候选排个序而已,反正候选都进来了,精排再差也不会丢东西。”
正解精排不仅要排序,还要做截断——只保留分数最高的 TopN,落选的候选直接消失。所以精排其实是最终“拍板”的守门员:它既决定顺序,也决定谁有资格上菜单。主厨不只是给菜排名,还会把不达标的菜撤下桌。
为何常见:“精排”这个名字让人只想到“排”,而忽略了打分阈值与截断这个“筛”的动作。
误区“DeepFM 是不是像普通神经网络一样,把 FM 的输出接到 Deep 的输入上,一层一层加深?”
正解DeepFM 是并行结构:FM 分支负责显式的二阶交叉,Deep 分支负责高阶隐式特征组合,两个分支共享底层的 embedding 输入,最后把两者的输出相加再过 Sigmoid。二者不是串联关系;串行堆叠是 FNN 等早期模型的思路。
为何常见:很多结构图把两路画在一起,没强调“共享输入、并行相加”,读者容易按习惯理解成逐层堆叠。
误区“DIN 的注意力权重不是模型训练出来的吗?训练完就固定了,不管用户看什么商品,权重都不变。”
正解DIN 的注意力权重不是固定参数,而是动态计算出来的:每输入一个候选物品,注意力网络 g(v_i, v_target) 都会根据候选与历史行为实时算出一套新权重。换个候选就换一套权重——这正是“看人下菜”的关键。
为何常见:注意力机制里的“权重”一词容易让人联想到训练得到的参数,实际上它是随输入变化的中间计算结果。
误区“线上日志记录了用户看了什么、点了什么,直接打标切出来训练不就行了?”
正解线上日志只有“曝光 + 反馈”信息,存在严重的曝光偏差:没曝光过的好商品永远没有正样本机会。所以必须引入随机采样负样本补充,还要按时间窗口切分防止数据泄漏。主厨的笔记只记了“上桌的菜”,得自己补上“没上桌的参考评价”。
为何常见:日志看似是天然标注数据,但推荐场景的反馈是“选择性曝光”产生的,不是随机实验,初学者很容易忽略这个偏差。
误区“反正是深度学习模型,把能挖的特征全堆进去,数据量大肯定能学好。”
正解特征过多会带来训练变慢、过拟合、在线获取成本高、稀疏特征冷启动难等问题。实际工程要评估每个新增特征的边际收益,做特征筛选与降维。精排讲究“有效特征”,而不是“全部特征”。
为何常见:深度模型给人“自动学习”的印象,让人误以为特征工程不再重要;但特征质量直接决定模型拟合的上限,主厨档案记得再厚,记一堆无关信息反而干扰判断。
面试题
Q1. 精排模型在推荐流程中的核心职责是什么?
- A.从千万级全量物品中快速初筛候选
- B.对召回候选精细化打分并决定最终顺序← 正确答案
- C.负责用户画像的实时更新与存储
- D.为物品建立索引并执行向量检索
答案:B。精排的输入是召回候选,输出是精细化分数(通常为 pCTR),并据此排序与截断。A、D 属于召回与向量检索的职责,C 属于画像系统。就像品鉴主厨只对采购团队送来的候选菜负责,不负责买菜和记账。
Q2. DeepFM 中 FM 分支的核心作用是什么?
- A.仅捕获特征之间的一阶线性组合
- B.显式建模特征之间的二阶交叉← 正确答案
- C.对用户历史行为序列做注意力加权
- D.负责将输出压缩到零到一区间
答案:B。FM 用隐向量内积显式建模二阶特征交叉,例如“咸度 × 甜度”这种搭配关系。一阶线性只是 FM 的组成部分而非核心;C 是 DIN 的注意力机制;D 是 Sigmoid 的职责。
Q3. 精排阶段会用到下列哪些类别的特征?
- A.用户的年龄性别与历史行为← 正确答案
- B.候选物品类目价格与质量分← 正确答案
- C.请求时刻的时段地点与设备← 正确答案
- D.召回传递来的粗排分与热度← 正确答案
答案:ABCD。用户、物品、上下文三类基础特征加上召回侧传递的特征,都是精排的常见输入。品鉴档案维度越全,主厨打分越可靠;召回阶段的粗排分、热度等也常作为精排的补充特征。
Q4. DIN 的注意力权重是如何产生的?
- A.训练后固化下来的全局静态参数
- B.由候选与历史行为动态计算得出← 正确答案
- C.取所有历史行为权重的平均值
- D.由物品热度直接决定权重大小
答案:B。DIN 的注意力网络 g(v_i, v_target) 在每次输入候选物品时实时计算权重,换一个候选就换一套权重。这正对应“看人下菜”——主厨的关注点随食客口味动态变化,而不是一套固定标准。
Q5. 为缓解曝光偏差,精排负样本常用的补充方式是?
- A.仅使用曝光未点击的负样本
- B.随机采样未曝光物品做负样本← 正确答案
- C.从正样本中反向生成负样本
- D.使用用户点击过的物品做负样本
答案:B。日志只有曝光样本,若只用曝光未点击当负样本,模型学不到“没曝光的好物品”。随机采样未曝光物品补充负样本,能让模型在全量候选上更公平地排序,相当于给品鉴笔记补上“没端上桌的菜”的参考评价。
Q6. 精排在线推理控制延迟的常用手段包括?
- A.对模型权重做 INT8 量化压缩← 正确答案
- B.缓存热点特征避免重复计算← 正确答案
- C.粗排与精排级联逐层过滤← 正确答案
- D.无限扩大候选集合并增加特征
答案:ABC。量化、特征缓存、级联过滤都是“高峰限时出菜”的实用手法。D 会反向增加计算量与延迟,违背延迟预算,是典型的错误方向。
Q7. 精排模型的打分输出通常通过哪个函数得到点击概率?
- A.ReLU 激活函数
- B.Sigmoid 激活函数← 正确答案
- C.Softmax 归一化函数
- D.Tanh 双曲正切函数
答案:B。点击率预估是二分类问题,用 Sigmoid 把分数压到 0~1 之间作为点击概率;Softmax 用于多分类,ReLU、Tanh 一般用于隐藏层激活,不直接输出概率。
Q8. 精排与召回在工作流程上的关系是什么?
- A.精排在召回之前进行全量粗筛
- B.精排以召回输出的候选为输入← 正确答案
- C.召回以精排结果作为候选池
- D.两者并行独立互不依赖结果
答案:B。整体流程是召回先“广撒网”产出候选,精排再“精雕细琢”打分排序并截断。就像采购团队在前、品鉴主厨在后,顺序不能颠倒。
Q9. 关于精排训练样本构建,正确的说法有哪些?
- A.特征只能取曝光时刻之前已有信息← 正确答案
- B.可以用点击之后才产生的标签特征
- C.样本按时间窗口划分防止数据泄漏← 正确答案
- D.样本越新越能反映当前用户兴趣← 正确答案
答案:ACD。特征必须取曝光前可得的信息,按时间窗口切分训练集与测试集,新样本更能反映兴趣变化,这些都对。B 属于典型的数据泄漏——相当于主厨写笔记时偷偷抄了明天的顾客点评。
Q10. 精排模型最核心的优化目标是什么?
- A.最大化单次推理的候选数量
- B.提升候选排序精度与业务指标← 正确答案
- C.最小化模型的参数量占用
- D.缩短召回阶段的检索链路
答案:B。精排的价值在于把排序做“准”,从而带动点击率、转化率等业务指标提升。追求候选数量是召回/粗排的目标,压缩参数量只是优化手段而非目的。