想象一条热闹的美食街。你站在路口喊了一声“来一份麻辣香锅”,几十家餐馆同时端出各自的“麻辣香锅”。这么多盘菜,哪一盘才是正宗好味道?你需要一位资深美食评委帮忙打分排名:哪家食材新鲜?哪家分量足?哪家口碑好?哪家出餐快?评委要看的每一件事——食材、分量、口碑、速度——就相当于排序学习里的“特征”。 搜索引擎的处境一模一样。用户输入一个查询词(点菜),互联网上有成千上万个网页(候选菜品),系统必须在瞬间给所有候选网页打分排序(评委打分)。可问题是:评委凭什么打分?总不能凭空说“我觉得这家好”。它需要具体、可测量的考察维度,这就是特征。而设计、挑选、打磨这些考察维度的整套功夫,就是特征工程。 特征工程就像给评委设计一份“评审表”:表上列什么维度、每个维度怎么量、单位如何统一、哪些维度真的管用、哪些维度可以组合出新味道。这一整套工作决定了评委能否快速准确地排出名次。实践中,特征工程往往决定了排序效果的上限——模型算法只是拼尽全力去逼近这个上限。
特征工程是排序系统的“燃料”:没有好燃料,再先进的发动机(模型)也只是空转。掌握特征工程,是搜索、推荐算法工程师的必备基本功。
目录
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
排序学习特征工程管线 ——从多源信息到排序特征 Q-D匹配:BM25/TF-IDF/编辑距离 统计:PageRank/点击量/文档长度 语义:Embedding相似度/BERT交叉 行为:CTR/停留时间/跳出率 特征 = f(query, doc, user, context) → 归一化 → 选择 → 组合 → 排序模型
特征分类:评审表先分板块
美食评委巡街前,不会只盯着“好不好吃”这一项。他会把评审表分成几个板块:这道菜本身怎么样(食材、火候),跟顾客点的菜对不对得上(点的是麻辣香锅还是日料),其他食客怎么看(排队人数、回购率)。分好板块,打分才有条理,不会顾此失彼。
排序学习的特征也是如此。业内通常按“来源与作用对象”把特征分成四大类:查询端特征、文档端特征、匹配特征、用户行为特征。查询端特征描述用户这次搜索本身,比如查询词长度、是疑问句还是名词短语;文档端特征描述候选网页自身的属性,比如标题长度、内容时效、页面质量;匹配特征刻画查询与文档的交互;用户行为特征来自历史反馈,比如点击率、停留时长。
这四类特征出现在同一份评审表上,共同决定每个候选文档的最终得分。用数学写出来,就是一条加权求和公式:
理解了这个“板块框架”,评委才知道该在哪个方向用力。特征分类正是特征工程的第一块拼图,它决定了后来所有特征从哪来、归哪组、怎么找。
- score(d,q)d 是候选文档(一盘菜),q 是用户查询(一张点单),score 是评委给这道菜打出的最终总分。
- f_i(q,d)第 i 个特征函数,也就是评审表上的第 i 个考核项,比如“词面匹配度”或“点击率”。
- w_i × f_i(q,d)w_i 是第 i 项的权重,表示这项对最终排名有多重要。权重乘以得分就是加权后的单项贡献。
- Σ_{i=1}^{n}从第 1 项加到第 n 项,把所有单项贡献汇总成最终得分。
匹配特征:菜与菜名对不对得上
评审在动筷子之前,第一反应是核对菜名:这道菜跟顾客点的“麻辣香锅”沾边吗?如果端上来一盘点心,做得再精致也是答非所问。搜索里最经典的匹配特征来自 TF-IDF。TF(词频)回答“查询词在这篇文档里出现了多少次”,IDF(逆文档频率)回答“这个词到底有多大区分度”。一个词在全网越稀有,一旦出现就越能说明文档确实在讨论它。
比如“麻辣”和“香锅”几乎每道川菜的标题都会提到,区分度很低;而“秘制酱料”很少见,一旦出现,这篇文档就很可能与用户要找的内容高度相关。TF-IDF 把这两者相乘,得到“文档对查询词面的契合度”:
词面匹配只是第一步,还有覆盖度(查询词有多少比例出现在文档标题中)、编辑距离(用户拼错字时还能找到对应文档)等配套特征。这些就像评委的“最基本核对清单”——先确认没端错菜,再谈好不好吃。
- tf(t,d)词频,统计词 t 在文档 d 中出现了多少次。出现越多,说明文档越可能在讨论这个词。
- df(t)文档频率,包含词 t 的文档数量。它用来衡量一个词是否常见。
- 分数 (N+1)/(df(t)+1)稀有度比值。N 是总文档数,比值越大说明词越稀有。分子分母各加 1 是为了防止除以零。
- log取对数把稀有度压缩到平缓范围,避免“非常稀有的词”获得爆炸式的权重。
- 整体相乘词频 × 稀有度,得出一个词对“查询-文档”匹配的贡献值。
统计特征:群众的筷子会投票
评委再厉害,也不可能尝遍美食街上每一道菜。他会在评审表上增加一栏:群众数据。有多少人点过这道菜、平均排队多久、回头客多不多——这些历史统计,是“用脚投票”的结果。
搜索里的统计特征正是这样的“口碑数据”:点击率(CTR)、停留时长、收藏/转发数、时效热度。其中点击率在排序特征中最为常见,公式是历史点击次数除以展示次数。但这里有个坑:数据稀疏。一盘新菜只被 3 个人尝过、2 人点赞,就是 66% 好评率,可信度太低。所以工程上会给分子分母加平滑系数,让样本量小的结果向“先验经验”收缩:
统计特征让评委“借大家的舌头打分”。但要注意:投票的人是在什么位置看到这道菜的,存在天然偏差。所以还需要继续看,怎么理解菜本身的“性格”。
- clicks历史上这个结果被点击的次数,来自用户反馈日志,相当于“受欢迎票数”。
- impressions这个结果被展示的总次数,分母越大说明统计越可靠。
- μ 与 λ两个平滑常数,相当于“先验经验”。它们保证只展示过一两次的结果不会因随机性得到极端估计。
- 整体除法分子分母都加上平滑项后做除法,得到修正后的点击率。样本越充足,估计越接近真实情况。
语义特征:读得懂字里行间
词面匹配记牢了“麻辣香锅”四个字,却听不懂“川味”“重辣”“牛油”这些近亲表达。评委如果只认字眼,就会错过那些真正懂行的人才懂的“同义菜”。
语义特征把这些“字里行间的意思”编码成向量。先用预训练模型把查询和文档分别映射成词向量或句向量,再计算两者的余弦相似度。余弦相似度衡量两个向量方向的接近程度。为什么用余弦而不是欧氏距离?因为欧氏距离对向量长度(比如文档长短)敏感,而余弦只看方向。查询和文档被塞进同一个向量空间后,语义相近的表达会自然聚拢——“麻”和“辣”即便不同词,向量也离得很近:
语义特征让评委听懂“行话”。但它依赖预训练模型,词面完全命中的强相关结果反而可能被稀释——所以语义与匹配两类特征在工业系统中通常同时使用、互为补充。
- a 与 b查询向量与文档向量,由词向量或句子编码器把文本变成一串数字。
- 点积 a·b把两个向量逐位相乘再求和,衡量“方向一致程度”。
- 模长 ||a||₂、||b||₂两向量的 L2 长度。除以模长相当于消除“文本长短”的影响。
- 整体比值余弦相似度取值在 [-1,1] 之间,越接近 1 表示语义方向越一致。
归一化处理:统一度量衡
评审表上各栏目单位五花八门:新鲜度满 10 分、重量用克、价格用元、排队用分钟。如果直接相加,一个 300 元的价差就能淹没 5 分的新鲜度差距——这显然不公平。
归一化就是把所有特征“换算到同一把尺子上”。最常用的是 min-max 缩放:把最小值映射为 0、最大值映射为 1,其他值按比例落在 [0,1] 区间。做完之后,每个特征都变成“相对所有样本的位置”。除了 min-max,还有 z-score 标准化、分箱(把连续值切成档位,如价格 0-100/100-200/200+)、缺失值处理(填充 0、中位数或单独设一档),它们都属于归一化处理家族:
统一度量衡之后,评委才能把“不同单位”的分数公平相加。这是所有后续加权与组合的数学前提——一锅汤里不能同时用“克”和“斤”两种秤来称盐。
- x某个样本的原始特征值,比如“排队 45 分钟”。
- min(x) 与 max(x)全体样本中该特征的最小值与最大值,它们决定新刻度的“0 点”和“满格”位置。
- 分子 x−min(x)当前样本离最小值有多远,回答“我在全体中的相对位置”。
- 整体比值除以取值范围长度后,任何特征都被映射到 [0,1],不同量纲从此可以直接比较。
特征选择:把干扰项划掉
评审表越填越长,评委反而犯了难:有些栏目每道菜都是满分,毫无区分度;有些栏目和别的栏目几乎重复;还有的栏目全看评委心情。这时必须做减法。
特征选择就是“去芜存菁”。常用方法有四类:方差阈值——取值几乎不变的特征直接剔除;相关系数——两个特征高度相关就保留其中一个;卡方检验——衡量特征与标签(相关性标注)是否独立,不独立的剔除;模型筛选——先用模型训练一版,看特征重要性再裁剪。其中相关系数是最直观的“冗余体检”:
去掉了噪音栏目,评审表变得更轻、更稳,评委也能把注意力集中在真正关键的味道上。特征选择省下的不仅是算力,更是过拟合的风险。
- X 与 Y两个特征,比如“点击率”和“收藏率”。
- cov(X,Y)协方差,衡量两特征是否同向波动。同涨同跌为正,此消彼长为负。
- σ_X 与 σ_Y两个特征各自的标准差,乘积用作“尺度归一”,消除量纲影响。
- 整体比值相关系数 ρ 落在 [-1,1]。|ρ| 越接近 1,说明两个特征高度冗余,去掉其一即可。
组合特征:一加一大于二
单看“辣度”和“麻度”都很普通,但“又麻又辣”才是这道菜的灵魂。评委渐渐明白:某些栏目放在一起看,才能揭示单看任何一个都发现不了的规律。
组合特征就是把两个或多个原始特征交叉起来创造新特征。经典组合方式有:乘积——比如“类目 × 价格带”;平方差——比如“查询词数量与标题词数量的差异”。平方差在排序中尤其常用,因为它显式告诉模型“两侧信息差得越多,越可能不匹配”。工程上还有特征哈希(把高维稀疏类别特征压缩成固定维度向量)和特征嵌入(让模型在训练中自动学习特征的低维表示),用来控制组合爆炸带来的维度灾难:
组合特征是调味的最后一步:同样一堆原始分数,能否调出“让人记住这道菜”的独特风味,就看这一手。评审表完成后,评委就能对整条美食街排出名次——这就是一次完整的特征工程流程。
- f_i 与 f_j两个原始特征,比如“查询词数量”和“标题词数量”。
- 差值 f_i−f_j刻画两者之间的差距,差距越大说明匹配度可能越差。
- 平方放大差距并消除正负号——无论谁大谁小都被公平对待,偏差越大的样本信号越突出。
AI 应用场景
电商搜索排序
阶段:特征工程(排序模型上线前的特征构建与迭代)
解决的问题:用户搜“连衣裙”时相关商品可能有上百万个,传统按销量排序只能看到爆款,忽略了不同人群的搜索意图。通过构造查询-标题匹配特征(词面重合 + 类目命中)、统计特征(近 7 天点击率、转化率)、组合特征(类目 × 价格带、风格词向量 × 人群偏好),让排序模型理解“通勤气质款”和“宽松休闲款”的差异,再根据点击反馈持续迭代。
具体案例:某电商团队为“连衣裙”查询增加“风格词向量 × 人群偏好”组合特征后,搜索页的点击转化率提升约 12%,长尾查询的商品曝光质量明显改善。
招聘网站人岗匹配
阶段:特征工程(职位与候选人的双向特征构建)
解决的问题:招聘平台给“后端工程师”岗位推荐候选人时,职位描述和简历用词差异很大(“扎实的 Java 功底” vs “熟悉 Spring 全家桶”),纯词面匹配召回效果差。需要构造技能词 TF-IDF 匹配特征、工作年限差值、活跃度、简历完整度、语义相似度等特征,统一做 min-max 归一化后送入排序模型。
具体案例:某招聘平台在候选人推荐场景中,加入“技能词 TF-IDF + 语义相似度”双通道特征后,简历投递转化率提升了约 15%,跨行业(如从 Java 转 Go)候选人的匹配召回也明显改善。
新闻信息流实时排序
阶段:特征工程(低延迟在线特征计算)
解决的问题:信息流场景每天要实时为百万篇文章打分,特征计算必须在毫秒级完成。高维类别特征(作者 ID、频道 ID)直接用会撑爆内存,连续特征(停留时长)又容易被离群值带偏。
具体案例:某新闻客户端使用特征哈希把作者、频道等稀疏类别特征压缩为固定长度向量,用分箱处理时长特征,再用方差阈值和相关系数做每周一次的特征选择,最终线上排序延迟降低 30%,模型效果保持稳定。
Python 演示
import math docs = [ {"id": "doc1", "title": "麻辣香锅做法 秘制酱料配方", "clicks": 820, "shows": 12000}, {"id": "doc2", "title": "川味火锅底料调制技巧", "clicks": 1530, "shows": 18000}, {"id": "doc3", "title": "北京烤鸭餐厅推荐", "clicks": 240, "shows": 15000}, {"id": "doc4", "title": "麻辣香锅加盟费用一览", "clicks": 610, "shows": 9000},] QUERY_WORDS = ["麻辣", "香锅", "做法"] def idf(word): df = sum(1 for d in docs if word in d["title"]) return math.log((len(docs) + 1) / (df + 1)) def compute_match(query_words, title): hit = 0.0 for w in query_words: if w in title: hit += idf(w) return hit / max(len(query_words), 1) def compute_ctr(doc): return doc["clicks"] / max(doc["shows"], 1) def min_max_normalize(values): lo = min(values) hi = max(values) if hi == lo: return [0.5 for v in values] return [(v - lo) / (hi - lo) for v in values] def main(): for d in docs: d["match"] = compute_match(QUERY_WORDS, d["title"]) d["ctr"] = compute_ctr(d) norm = min_max_normalize([d["match"] for d in docs]) for d, m in zip(docs, norm): d["match_norm"] = m d["final"] = 0.6 * d["match_norm"] + 0.4 * d["ctr"] docs.sort(key=lambda d: d["final"], reverse=True) for rank, d in enumerate(docs, 1): print("第%d名: %s 最终得分=%.4f (匹配特征=%.2f 点击率=%.4f)" % (rank, d["id"], d["final"], d["match_norm"], d["ctr"])) if __name__ == "__main__": main()
def compute_match(query_words, title):计算查询词与文档标题的匹配特征,对应正文的“词面契合度”。hit += idf(w)命中词按稀有度加权累加,“做法”这类信息量大的词贡献更高。math.log((len(docs) + 1) / (df + 1))逆文档频率,词在全网出现越少、权重越大。return hit / max(len(query_words), 1)除以查询词数做平均,防止长查询天然占便宜。d["ctr"] = compute_ctr(d)用点击量除以展示量得到原始点击率。(v - lo) / (hi - lo)min-max 归一化,把匹配特征映射到 0~1,统一度量衡。d["final"] = 0.6 * d["match_norm"] + 0.4 * d["ctr"]加权组合打分,匹配占大头、行为做校正。docs.sort(key=lambda d: d["final"], reverse=True)按最终得分降序排列,完成排序。
常见误区
误区既然是机器学习,把能想到的特征全部塞给模型,让它自己挑,肯定不会吃亏。特征数量越多,效果一定越好。
正解特征不是越多越好。这就像评审表上列了 100 个考察项,其中很多项在重复衡量同一件事(比如“盘子颜色”和“装修风格”),评委反而被噪声干扰。冗余特征会拖慢训练、放大过拟合风险。实际工程常用方差阈值、相关系数等方法把几十上百个特征砍到十几个核心维度,模型更稳、线上耗时更低。
为何常见:直觉上“信息量更多 = 更好”,没有意识到冗余和噪声特征会稀释有效信号,反而挤压了模型的泛化能力。
误区归一化就是把数据强行压到 0 到 1,这不就丢了原始大小信息吗?比如 30000 元客单价和 30 元客单价,归一化后看起来都差不多。
正解归一化丢失的是“绝对量纲”,保留的是“相对差异”。评委评分时从来不关心“这道菜用了 37.5 克盐”,只关心“在今晚所有菜品里它有多咸”。min-max 归一化把每个特征映射到 [0,1],保留样本间的相对距离,同时消除单位影响。真正需要谨慎的是对异常值敏感的数据,归一化前通常先做异常值截断。
为何常见:把“数值大小”和“信息”画等号,而排序学习真正关心的是样本之间的相对位置,不是绝对刻度。
误区既然深度学习能理解语义,TF-IDF 这种词频统计是不是已经过时了?只用语义特征就够了。
正解语义特征擅长“意会”,匹配特征擅长“言传”。用户搜“麻辣香锅”时,任何不含这几个字但语义近似的文档,都能靠语义特征召回;但词面精确命中的文档通常是最强相关(比如菜谱标题本身就叫“麻辣香锅做法”)。工业场景中两者互补:匹配特征保证精准,语义特征补充相关,一起作为双保险。
为何常见:听到“语义”就觉得更高级,忽略了精确匹配在搜索场景中的底线价值——用户输入的关键词本身就是最强的相关信号。
误区点击率(CTR)高就说明用户喜欢这个结果,直接按 CTR 排序不就行了?为什么还要搞这么多特征?
正解点击率有两个致命陷阱。一是位置偏差:排在第一的结果天然更容易被点击,并不代表它质量最高。二是数据稀疏:新品展示 1 次、点击 1 次就有 100% CTR,完全不可信。所以要加平滑系数(先验经验),做位置去偏,并且把 CTR 和其他特征一起交给模型,而不是单独用它拍板。
为何常见:把“相关性”误当成“因果性”,忽略了用户行为受展示位置和曝光量的强烈干扰。
误区单个特征不够,那就把两两组合、三三组合全做出来一起喂给模型,反正多总比少好。
正解盲目组合会造成组合爆炸:100 个特征两两交叉就有 4950 个新特征,训练数据根本扛不住,还会引发严重的过拟合。组合特征必须带着业务假设去做——比如“类目 × 价格带”,是因为 3C 用户对价格更敏感,这种有意义的交叉才有价值。上线前还要用特征选择或 A/B 实验验证组合是否真的有效。
为何常见:把算力当万能解药,忽视了特征工程本质上是“业务理解 + 实验验证”的循环,而不是无脑的排列组合游戏。
面试题
Q1. 在 Learning to Rank 特征工程中,“查询词与网页标题的重合程度”属于哪一类特征?
- A.查询端特征
- B.文档端特征
- C.查询匹配特征← 正确答案
- D.用户行为特征
答案:C。匹配特征描述查询与文档之间的相互作用。就像“顾客点的菜”与“店里的菜色”是否合拍,取决于两者组合而不是任何单独一方。标题重合度既不是纯查询属性,也不是纯文档属性,因此属于匹配特征。
Q2. 某特征最小值为 0、最大值为 200,经 min-max 归一化后,原始值 50 变为多少?
- A.0.15
- B.0.25← 正确答案
- C.0.50
- D.0.75
答案:B。(50−0)/(200−0) = 0.25。归一化把特征映射到 [0,1],相当于把“500 克盐”换算成“占满分的比例”,保留相对位置、消除量纲差异。代入公式即可得结果。
Q3. 以下关于 TF-IDF 中 IDF 的说法,正确的是?
- A.出现越多 IDF 越大
- B.与查询长度正相关
- C.出现越少 IDF 越大← 正确答案
- D.与文档频次无关
答案:C。IDF = log((N+1)/(df+1)),df 越小比值越大,IDF 越大。就像“松露”比“猪肉”更能体现一道菜的特色,稀有词区分度更强。A 说反了,B 与查询长度无关,D 否认了与文档频次的关系,均错误。
Q4. 下列关于归一化的说法,正确的有?
- A.归一化可以把不同量纲的特征统一到同一尺度← 正确答案
- B.min-max 归一化对异常值天然不敏感
- C.归一化后特征取值范围通常落在 [0,1]← 正确答案
- D.归一化一定会改变样本之间的大小次序
答案:AC。A、C 正确。min-max 归一化是线性变换,把特征映射到 [0,1]。B 错误,异常值会拉大 max−min,使正常值挤在很小的区域;D 错误,线性变换保持相对大小次序,就像评委换一种计分刻度,名次不变。
Q5. 特征 A 与特征 B 的皮尔逊相关系数接近 0.98,特征工程中合理的处理是?
- A.保留其中一个避免冗余← 正确答案
- B.两个都保留信息更多
- C.直接把两个特征相乘
- D.将两个特征都删除
答案:A。|ρ| 接近 1 说明两特征几乎携带相同信息,同时保留会放大单方向信号并增加过拟合风险。评审表上“分量”和“菜量”是同一个意思,留下一个就够了。B 是常见误区,C、D 缺乏依据。
Q6. 在 LTR 特征工程中,构造组合特征的正确做法包括?
- A.基于业务假设做有意义的交叉← 正确答案
- B.把所有特征随意两两相乘
- C.用特征选择与实验验证有效性← 正确答案
- D.组合特征维度可以无限增长
答案:AC。组合特征必须带着业务假设(如“类目 × 价格带”),并通过特征选择或 A/B 实验验证。盲目交叉和维度爆炸会带来过拟合与性能退化,就像调酱汁要懂“豆瓣酱配甜面酱”的风味逻辑,而不是把调料全倒在一起。
Q7. 用户行为特征(如点击率)的主要风险是?
- A.数据量太大难以存储
- B.无法通过接口获取
- C.与用户查询词无关
- D.位置偏差与数据稀疏← 正确答案
答案:D。点击率受展示位置影响(排第一更容易被点),且新结果曝光少、点击率极不可靠,因此要平滑和去偏。就像评价一家新店,只看 3 个人的好评没有说服力,还要考虑它在榜单位置带来的天然流量。
Q8. 工程师构造组合特征 (f_i−f_j)² 主要是为了?
- A.消除特征的量纲差异
- B.直接降低特征的总维度
- C.捕捉两者的非线性差异← 正确答案
- D.把特征变成离散分类值
答案:C。平方差组合把“两个特征相差多大”显式交给模型,平方消除正负号并放大大偏差,属于非线性交互。消除量纲靠归一化(A 错);组合特征通常增加维度(B 错);平方差仍是连续值,不是分类变量(D 错)。
Q9. 常用的特征选择方法包括?
- A.方差阈值剔除几乎不变特征← 正确答案
- B.相关系数剔除冗余特征← 正确答案
- C.卡方检验衡量特征独立性← 正确答案
- D.随机删除部分低维特征
答案:ABC。方差阈值、相关系数、卡方检验都是经典筛选方法,分别针对“无区分度”“冗余”“与标签无关”三类问题。随机删除没有依据,会破坏信息完整度,不属于正规方法。
Q10. 关于语义特征与匹配特征的关系,下列说法最准确的是?
- A.语义特征可完全替代匹配特征
- B.语义与匹配互补提升排序效果← 正确答案
- C.匹配特征可完全替代语义特征
- D.两者互斥只能选一个使用
答案:B。词面匹配保证精确、语义相似保证相关,两者互补。完全替代的说法忽略了“意会”与“言传”各自的边界:搜“麻辣香锅”时菜谱标题的字面命中不可放弃,而“川味火锅”这类同义表达又必须靠语义。实际系统往往同时上两组特征,用实验决定权重。