街角的“老王小吃店”门口挂着三块招牌:卤味、炒粉、甜品。老王练就了一双“人肉预估眼”——客人路过时,他要在零点几秒内判断:这位客人会为哪块招牌停下?进店后会不会掏钱?这几乎是广告系统每天在做的事:CTR 预估。客人还没进店,老王手头只有线索:性别年龄、天气时段、之前来过几次、上次点了什么。老办法是盯单一线索;后来他学会把“下雨”和“上班族”两条线索摆在一起看,命中率立刻提升——这是特征交叉;再后来他学会翻客人点单历史,却只挑与今天菜品相关的记忆重点参考——这是注意力机制。DeepFM 就是“擅长组合线索”的老王,DIN 就是“会挑重点翻旧账”的老王。本文用小吃店的生意故事,把这两个模型拆开揉碎讲透。
掌握 DeepFM 与 DIN,你就具备了设计广告与推荐系统核心排序模型的能力——这是决定一家平台“变现效率”与“用户体验”的关键技能,也是广告算法工程师岗位的核心竞争力。
目录
可视化演示
展示DeepFM模型的FM二阶特征交叉与DNN高阶交叉架构、DIN注意力机制对用户行为的加权建模、以及CTR/CVR预估漏斗与样本选择偏差。
本节配有交互动效(进入视口后自动加载,约 3KB)。
DeepFM模型架构 FM组件:二阶特征交叉,学习特征间隐式交互 DNN组件:高阶特征交叉,通过全连接层学习非线性组合 共享Embedding层:FM和DNN共用底层特征嵌入 y = sigmoid(FM(x) + DNN(x)) DeepFM端到端联合学习低阶与高阶特征交叉。
CTR预估背景:门口挂牌,先算进店率
老王每天傍晚都会算一笔账:今天路过一千人,进店一百人,其中三十人点了卤味。但他真正关心的是“每一位路人走进来”的概率,而不是等人进门后才统计。广告系统完全同理:用户目光扫过页面的瞬间,平台必须在几十毫秒内预估出“这条广告被点击”的概率,这个概率就是 CTR(点击率,Click-Through Rate)。
点击率预估算的是条件概率:给定用户特征 x——年龄、性别、设备、时段、历史行为,以及广告特征——类目、创意、出价,模型输出 P(点击 | x)。广告位的竞争不是先到先得,更像老王的菜单展示位:位置有限,谁最有潜力带来生意?平台不按单个点击算账,而是按 eCPM(千次展示预期收益)排序,预测点击率越高、广告主出价越高的广告,越容易赢得位置。
如果 pCTR 估得不准会怎样?老王把一个不想吃辣的人热情招呼进店,客人扭头就走——平台展示了用户不感兴趣的广告,伤害体验;反过来,一个正想找卤味的人被忽略,平台丢了收入。因此 CTR 预估既是“收入引擎”也是“体验守门员”。
一句话:CTR 预估就是老王在客人进门前的“一瞥定生意”——这一瞥的质量,直接决定招牌有没有挂对、食材有没有备足,以及今天到底能不能赚到钱。
- 预测概率pCTR 是模型预估的点击概率,相当于老王对客人进店率的那一瞥。
- 出价含义bid 是广告主愿意为单次点击支付的金额,代表这条广告在广告主心中的价值。
- 千次折算把单次收益换成一万次展示口径,便于跨广告、跨位置比较,避免展示量差异带来的错觉。
- 排序依据平台把候选广告按 eCPM 从高到低排序,高的进展示位,低的被淘汰。
特征表示基础:大本子与画像速写
老王的记事本有几百页,每页是一位客人的特征表:性别、职业、天气、来店时段。老式登记法是“一栏打一个勾”——客人是女性,就在“女性”格子打勾。这种“打勾”记录方式,在 AI 里叫独热编码(one-hot encoding):每个特征值占一个维度,出现即置 1,其余全为 0。
问题在于格子实在太多:广告系统里的特征可能上亿个,可一个样本往往只打了十几个勾,向量又长又空,这就是“稀疏”。老王不会把一百个格子原样背下来,他会写一句“画像速写”:25 岁出头、重口味、雨天爱来。“速写”在模型里就是嵌入(Embedding):把稀疏的独热向量压缩成一个小维度的稠密向量。
嵌入的价值有二:其一,维度从百万级降到几十几百,计算量骤减;其二,语义相近的特征——“爱辣”与“爱重口”——在向量空间中彼此靠近,模型可以把一个特征学到的规律“借”给相似特征,这就是泛化能力。
回扣到故事:老王不用背几百页打勾本,只看几行速写就能记住每位客人。嵌入就是模型的“速写本”,也是后面一切模型的地基。
- 稀疏向量x 是独热编码,长度可达数百万,但大多数位置为 0。
- 嵌入矩阵W_emb 的每一行对应一个特征值的向量表示,由模型训练自动学习,不需要人工设计。
- 稠密向量e 是压缩后的低维向量,通常几十维;语义相近的特征在空间里距离更近。
FM分解机原理:两条线索拉手,生意翻倍
老王发现单一线索经常骗人:“周五”单独看,外卖客多堂食客少;“雨天”单独看,客人懒得动。但“周五 + 雨天”组合在一起,外卖订单暴涨。真相藏在组合里。
特征交叉就是组合效应。最笨的办法是把每两个格子的勾直接合成新特征——但组合数量是平方级爆炸,绝大多数组合在训练数据里从未出现过,根本统计不出参数。FM(因子分解机,Factorization Machine)换了个聪明思路:不为每个组合学一个独立参数,而是给每个特征学一个小向量——隐向量 v_i。两个特征是否“合拍”,用它们向量的内积来衡量。
内积大 = 两个特征总是一起出现且倾向点击。更妙的是:即便某个组合从未在数据里露过面,只要“周五”和“雨天”各自在其他组合中出过场,模型依然能推断它们合不合拍。这正是 FM 在稀疏数据下敢做二阶交叉的底气。
回扣一句:FM 相当于老王把记事本里所有线索两两拉手试一遍,不必等“某一对组合真实发生够多次”,只要两条线索各自有故事,他就能预判这对组合的化学反应。
- 全局偏置w_0 表示全体样本的基础点击倾向——相当于街上路人的平均进店率。
- 一阶项每个特征单独说话,x_i 是该特征值(0 或 1),w_i 是它的单打影响力。
- 二阶交叉项对所有两两组合求和,自动捕捉“周五×雨天”这类双线索组合。
- 隐向量内积v_i 与 v_j 的内积越大,说明这两个特征越“合拍”,共同出现越容易带来点击。
DeepFM架构:左右手联合作战
老王招了个新助手阿强。老王自己擅长看两两组合——这是 FM;阿强脑子活,擅长从一堆琐碎信息里抽丝剥茧、看到全局模式——这是深度网络 Deep。每天后厨碰头,老王说“我觉得这单合适”,阿强说“我的直觉也加分”,最后两人意见相加拍板——这就是 DeepFM 的“并联”结构。
DeepFM = FM 部分 + Deep 部分,两者共享同一份特征嵌入,各自输出一个分数再加起来,过一个 sigmoid 激活变成 0 到 1 之间的点击概率。
为什么必须要有 DNN?FM 只管二阶交叉,可现实中“周三深夜的网吧里,年轻人刷到电竞周边广告”这种复杂套路,需要更多层的非线性去摸索。Deep 路径通过多层全连接自动挖掘高阶特征交互;共享嵌入则让两条路径互相当外援——FM 学到的干净表示帮 DNN 加速收敛,DNN 的高阶抽象反过来帮 FM 找到更合适的隐向量。
回扣故事:老王负责快准稳的两两判断,阿强负责深挖复杂套路。两人共用同一本速写本、不抢数据,最终打包成一个综合意见。也正因如此,DeepFM 在工业界广告 CTR 预估中成为标杆级方案,被广泛应用在电商与内容平台的排序系统里。
- 低阶信号y_FM 是 FM 路径输出的分数,负责二阶交叉。
- 高阶信号y_DNN 是深度路径输出的分数,负责高阶非线性模式。
- 概率化σ 是 sigmoid 函数,把两者之和压到 0~1,变成可解释的点击概率。
DIN注意力机制:手电筒照最相关的记忆
客人小张上个月来了八次,每次点的都不一样。老王要预测他今天会不会买卤味——小张的全部历史该被平均看待吗?当然不。如果小张昨天刚点了卤味,这条记录就该被高高亮起;半年前那次甜品记录,在今天参考价值就很低。DIN(Deep Interest Network,深度兴趣网络)做的事,就是教老王端起“今天的菜”当手电筒,去逐一扫描小张的历史记录。
所谓注意力(Attention),就是给每条历史行为算一个权重。权重由“当前候选广告”与“该条历史行为”的相关度打分得到,然后不是简单平均,而是加权求和——把历史行为聚合成一个被“今天的需求”调过视角的向量。
注意力参数和整个模型一起用点击目标做梯度更新——手电筒照哪里、照多亮,是模型自己学出来的,而非人工规则。
所以 DIN 让老王记住的不是流水账,而是一本会随着“今天菜单”自动翻页的记忆账本。这也是广告系统个性化的精髓:同一段历史,换成卤味候选和换成甜品候选,模型注意到的重点完全不同。
- 打分函数f 把历史行为 e_i 和候选广告 e_a 拼起来或做内积,再经过激活层,输出一个原始相关分。
- 指数化exp 保证分数为正,同时拉大高分与低分的差距。
- 归一化分母对所有历史行为分数求和,让每个权重落在 0~1 且总和为 1。
- 加权池化用权重 a_i 对历史行为向量加权求和,得到“带着今天视角”的兴趣向量。
模型对比取舍:两个掌柜各有所长
DeepFM 是“综合型掌柜”:特征交叉广、结构稳、什么场景都能顶。DIN 是“记忆型掌柜”:擅长消化海量用户行为序列,个性化更强。现实中两家店往往不是二选一,而是搭伙——先用 DeepFM 打底排序,再叠加 DIN 做个性化精排。
结构上的关键差异在于输入角色。DeepFM 的输入是“全量特征”,用户、广告、上下文平等进入,一并做交叉;DIN 的输入里有一组特殊角色——用户历史行为序列,它用候选广告作引导去压缩历史,天然带有“个性化视角”。
另一维取舍是复杂度。FM 的参数量大约等于全局偏置 1、一阶权重 n、隐向量参数 nk 之和,是线性规模,工业界非常轻量;DIN 额外多出打分网络与注意力计算,训练和推理成本更高,换来的是行为维度的表达力。
选型口诀:特征稀疏但行为数据少,优先 DeepFM;行为序列长、个性化诉求强,上 DIN;最成熟的工业方案常常是“双掌柜同堂”——DeepFM 负责全面,DIN 负责走心。
- 偏置参数1 个全局偏置 w_0。
- 一阶权重n 个特征各配一个线性权重。
- 隐向量规模n 个特征各配一个 k 维隐向量,共 nk 个参数。
工程与评估:试卖一周,分人群看口碑
新品推出不能马上全店换菜单,先试卖一周。老王的评价方式不是只看总流水——因为熟客、过路客、外卖平台的客群结构完全不同,总流水上涨可能是恰逢节假日。正确做法是分人群、分时段地看。
广告评估同理。CTR 模型离线先看 AUC:随机抽一个正样本(被点击)和一个负样本(未被点击),模型把正样本排得更高的概率。但整体 AUC 有个缺陷:它把不同用户的样本混在一起比,而广告业务真正关心的是“在同一个用户面前,广告排序对不对”。
工程上还要管住两件事:一是数据漂移——训练分布和线上实时分布会逐渐错位,需要周期性重训;二是延迟反馈——用户可能点完广告很久才转化,标签不能即时到位。最终判定胜负靠的是线上 A/B 实验。
模型上线不是终点,而是“试卖开场”。用 GAUC 分人群打分,用 A/B 实验对生意说话——老王也学会了这套:先小范围试卖,看熟客和过路客各自的反馈,再决定要不要全店推广。
- 分组 AUC每个用户组内单独计算排序质量。
- 组权重w_i 通常取该组曝光量占总曝光的比例,曝光越多说得越算数。
- 加权求和把各组指标汇总成一个贴合业务分布的整体指标。
AI 应用场景
场景一:电商信息流广告的粗排与精排
阶段:在线推理与排序
解决的问题:传统逻辑回归只能对特征做线性加权,广告主投放“新客+美妆”这类组合时,平台无法自动捕捉组合规律;DeepFM 自动完成特征交叉,把新旧客、类目、时段等组合效应挖出来,CTR 预估更准,千次展示收入明显提升。
具体案例:某电商 App 在双 11 大促期间,高峰期每秒百万级请求,DeepFM 在精排环节对召回后的候选广告逐一打分排序,并配合剪枝与控制嵌入维度,把单次推理延迟压在 50 毫秒以内,平台 GMV 与广告收入双涨。
场景二:重定向广告中的个性化历史行为加权
阶段:个性化精排
解决的问题:用户曾浏览过二十个商品,传统做法把所有行为平均成一个向量,真实偏好被稀释;DIN 用当前候选广告与每条历史行为算注意力,精确捕捉“最近在看的同类商品”。
具体案例:用户三天前看过运动鞋,今天回到平台闲逛,DIN 让“运动鞋”相关的历史记录权重远高于其他浏览记录,重定向广告点击率提升了约 15%,平台猜对了用户此刻的意图。
场景三:程序化广告中的实时竞价出价
阶段:出价决策
解决的问题:DSP(需求方平台)要在几十毫秒内决定出价,光有 pCTR 还不够,要把它乘以广告主价值并考虑预算约束;把 DeepFM/DIN 输出的 pCTR 作为出价分母与分层依据,可让预算花在最值得的流量上。
具体案例:广告主给出固定日预算,希望“高点击概率的流量多花钱、低概率流量少花钱”。策略按 pCTR 分档调价后,整体投放 ROI 提升约 12%,预算消耗也更平稳。
Python 演示
import numpy as npimport torchimport torch.nn as nnfrom sklearn.metrics import roc_auc_score torch.manual_seed(42)np.random.seed(42) N, D, K = 2000, 20, 4X = np.random.randint(0, 2, size=(N, D)).astype(np.float32)w = np.random.randn(D, 1)pair = np.random.randn(D, D) * 0.3logit = X @ w + ((X @ pair) * X).sum(axis=1, keepdims=True)y = (1.0 / (1.0 + np.exp(-logit)) > 0.5).astype(np.float32) class DeepFM(nn.Module): def __init__(self, dim, k): super().__init__() self.w0 = nn.Parameter(torch.zeros(1)) self.w1 = nn.Parameter(torch.randn(dim, 1) * 0.1) self.V = nn.Parameter(torch.randn(dim, k) * 0.1) self.dnn = nn.Sequential( nn.Linear(k, 32), nn.ReLU(), nn.Linear(32, 16), nn.ReLU(), nn.Linear(16, 1) ) def forward(self, x): first = self.w0 + x @ self.w1 interm = ((x @ self.V) ** 2 - (x ** 2) @ (self.V ** 2)).sum(1, keepdim=True) * 0.5 deep = self.dnn(x @ self.V) return torch.sigmoid(first + interm + deep) model = DeepFM(D, K)opt = torch.optim.Adam(model.parameters(), lr=0.01)lossf = nn.BCELoss()Xt = torch.from_numpy(X)yt = torch.from_numpy(y) model.train()for epoch in range(30): opt.zero_grad() loss = lossf(model(Xt), yt) loss.backward() opt.step() if epoch % 10 == 0: print("epoch %d loss %.4f" % (epoch, loss.item())) model.eval()with torch.no_grad(): prob = model(Xt).numpy().ravel()print("AUC %.4f" % roc_auc_score(y, prob))
logit = X @ w + ((X @ pair) * X).sum(axis=1, keepdims=True)生成同时含一阶效应与二阶组合效应的模拟点击标签,保证 FM 有规律可学。self.V = nn.Parameter(torch.randn(dim, k) * 0.1)为每个特征分配一个 k 维隐向量,这是 FM 交叉的“合拍度”参数。interm = ((x @ self.V) ** 2 - (x ** 2) @ (self.V ** 2)).sum(1, keepdim=True) * 0.5用恒等变换高效计算所有二阶交叉的和,复杂度从 O(n²) 降到 O(nk)。deep = self.dnn(x @ self.V)Deep 路径吃的是稀疏特征经嵌入后的稠密表示,而不是原始 one-hot。roc_auc_score(y, prob)用 AUC 衡量模型把点击样本排在未点击样本前面的能力。
常见误区
误区我一直以为 DeepFM 里的 DNN 就是把上亿维的原始 one-hot 稀疏向量直接塞进全连接层,反正深度学习什么都能学。
正解DNN 的输入是“共享嵌入后的稠密向量”。原始 one-hot 输入会让第一层参数爆炸、梯度稀疏、训练极慢。老王不会让阿强直接读几百页打勾大本子,而是先让阿强看速写本(嵌入),才能快速找到规律。结构图上的“稀疏输入”实际要先过嵌入查表层。
为何常见:因为 DeepFM 结构图往往从“稀疏输入”直接画线到两路分支,很多人忽略了图里嵌入层这个隐形起点。
误区DIN 训练完成后,每条历史行为的重要性权重就存进模型里了,预测时直接查表取用。
正解注意力权重是“输入相关”的动态值:每次预测都要用当前候选广告与每条历史行为重新计算。同一段历史,候选换成卤味和换成甜品,权重完全不同。DIN 学的是“如何打分”的函数,而不是“谁重要”的静态参数表。
为何常见:把注意力机制混同于普通网络权重,忽略了注意力是在前向过程中实时计算出来的。
误区FM 里全是“格子打勾”的类别特征,像“年龄 25 岁”“湿度 80%”这种连续数值不能进 FM。
正解连续特征可以直接以 x_i = 数值参与计算,例如“降雨量 12 毫米”照样能与“外卖时段”拉手交叉;也可以做分桶后转成 one-hot 再交给 FM。老王记下的“下两小时雨量 12mm”完全不影响它和“雨天”这个离散特征合拍。
为何常见:教材例子多用离散特征演示,初学者容易误解连续特征被模型排除在外。
误区模型在测试集上 AUC 从 0.72 涨到 0.74,我就可以放心上线了。
正解广告排序关心的是“同一个用户面前”正负样本的顺序。整体 AUC 混入了大量跨用户比较,数值可能虚高。应优先看 GAUC(按用户分组加权的 AUC),再结合线上 A/B 实验确认收益。老王不会因为整条街总流水上涨就高兴,他会分开看熟客、过路客、外卖客的口碑。
为何常见:离线指标直观好刷,容易忽略 CTR 业务天然的“组内排序”属性。
面试题
Q1. DeepFM 名字里的 FM 主要负责什么?
- A.处理稀疏特征的一阶线性部分
- B.捕捉特征间的二阶交叉组合← 正确答案
- C.对广告进行实时竞价排序
- D.将点击概率转为出价金额
答案:B。FM 的核心价值在于二阶交叉项——用隐向量内积衡量两两特征的合拍程度。A 是 FM 的一阶项但并非其独特职责,C 是广告系统排序环节,D 是出价策略,均不归属 FM。
Q2. 广告排序公式 eCPM = pCTR × bid × 1000 中,bid 的含义是?
- A.广告主愿意支付的单次点击出价← 正确答案
- B.用户过去三十天的历史点击次数
- C.模型预估的当前点击概率数值
- D.平台每千次展示的固定收入
答案:A。bid 是广告主为一次点击愿意付出的钱。pCTR 才是模型预估的点击概率(C),历史点击次数(B)与 eCPM 无关,平台收入(D)正是 eCPM 本身而非 bid。
Q3. FM 中隐向量 v_i 与 v_j 的内积用于表达什么?
- A.单个特征在全局的重要程度
- B.两两特征之间的组合亲和度← 正确答案
- C.样本属于正类的预测概率
- D.嵌入矩阵的参数压缩比例
答案:B。内积越大表示特征 i 与 j 越“合拍”,即组合出现时越倾向点击。A 由一阶权重表达,C 是整体模型的输出,D 与隐向量内积无关。
Q4. DeepFM 中 DNN 路径的输入是什么?
- A.全部原始独热的稀疏拼接向量
- B.所有特征经过嵌入后的稠密拼接← 正确答案
- C.FM 路径输出的预测概率标量
- D.用户最近三十天行为序列
答案:B。DeepFM 的两条路径共享特征嵌入,DNN 吃的是嵌入后的稠密向量拼接。直接吃原始稀疏向量会导致参数爆炸与训练困难,C 把两条路径串起来了,D 是 DIN 的输入形态。
Q5. DIN 的注意力权重由谁决定?
- A.训练结束后固定的离线参数表
- B.候选广告与历史行为的相关度← 正确答案
- C.广告主为本次点击的出价高低
- D.特征在样本中出现频率对数
答案:B。DIN 每次预测都用当前候选广告向量与每条历史行为向量计算相关度,再 softmax 归一化得到权重。权重是动态计算的,不是固定参数,与出价和词频无关。
Q6. DIN 中 softmax 对注意力分数归一化的核心作用是?
- A.让所有分数总和等于模型的参数个数
- B.使权重和为 1 便于加权求和← 正确答案
- C.把所有出现的负数分数全部置零
- D.将打分结果的方差自动调大
答案:B。softmax 把原始分数变成总和为 1 的概率分布,这样加权池化结果相当于“带权重的历史兴趣平均”。它不会记住参数个数,也不只是置零负数,更不是调大方差。
Q7. 广告 CTR 评估中,为什么要用 GAUC 而非直接看整体 AUC?
- A.按用户分组再加权反映组内排序质量← 正确答案
- B.计算复杂度比整体 AUC 更低更快
- C.不需要任何真实点击标签即可计算
- D.数值上永远比整体 AUC 大得多
答案:A。广告业务关心“同一用户面前广告排序是否合理”。GAUC 先分组算 AUC,再按曝光量加权,避免跨用户比较带来的虚高。B、C、D 对 GAUC 的描述均不符合事实。
Q8. 下列哪些属于 DeepFM 的两个组成部分?
- A.FM 分解机进行低阶特征交叉← 正确答案
- B.Deep 神经网络挖掘高阶特征← 正确答案
- C.对历史行为做注意力池化
- D.用协同过滤矩阵分解用户
答案:AB。DeepFM 由 FM 路径和 Deep 路径并联组成,分别负责低阶二阶交叉与高阶非线性模式。C 是 DIN 的组件,D 是传统协同过滤的思路,均不属于 DeepFM。
Q9. 关于特征嵌入(Embedding),以下说法正确的有?
- A.把稀疏独热映射为稠密向量← 正确答案
- B.嵌入矩阵训练后参数保持不动
- C.嵌入向量维度远小于独热维度← 正确答案
- D.嵌入只能用在连续数值特征上
答案:AC。嵌入把高维稀疏独热压缩成低维稠密向量,维度通常远小于独热长度。嵌入矩阵随训练持续更新(B 错),且主要用于离散类别特征(D 错)。
Q10. 关于 DeepFM 与 DIN 的对比,正确的有?
- A.两者都从稠密嵌入出发做预测← 正确答案
- B.DIN 用候选广告引导行为加权← 正确答案
- C.DeepFM 完全不需要任何特征处理
- D.两者最终都输出点击概率用于排序← 正确答案
答案:ABD。两者都先用嵌入把稀疏特征转成稠密表示,DIN 的注意力权重由候选广告与历史行为实时计算,两者最终输出点击概率参与广告排序。C 错误:DeepFM 仍需要清洗、筛选等基础特征工程。