从搜索排序到机器学习,我踩过的那些概念坑

孙红_程序员
2026-11-03 14:14
阅读 2457

在百度做搜索相关的算法,满打满算两年了。坐标成都,天府三街这边,生活节奏确实比北上广舒服不少。平时写代码主力是Vim,IDE基本不用——倒不是装逼,主要是刚入职那会儿带我的老哥是个Vim死忠,天天在我耳边念叨“手不离键盘才是工程师的浪漫”,耳濡目染就入了坑。现在连写Markdown都是终端里开个vim直接怼。

最近组里来了两个实习生,老板让我带带他们。我发现一个挺有意思的现象:这两个孩子代码能力都不错,LeetCode刷得飞起,但一问到机器学习的基础概念,比如“正则化到底在干嘛”“AUC为什么对样本不均衡不敏感”,就开始眼神飘忽。我寻思这不就是当年的我吗?刚转方向那会儿,天天把“模型”“特征”“损失函数”挂嘴边,真让我用大白话解释清楚,我也支支吾吾。

所以就有了这篇东西。不整那些高大上的公式推导,就聊基础概念,顺便穿插一些我在实际业务中踩过的坑。对了,最近组里在折腾AI辅助写代码的事,试了Codex和Aider,文末也聊聊这些工具对算法工程师的影响。

机器学习到底在干嘛

一句话:从数据中自动学出一个函数,这个函数能对没见过的输入给出靠谱的输出。

比如搜索场景里,用户输入一个query,我们要预测这个query下每个候选文档的点击概率。传统做法是人工定规则——“标题完全匹配加10分”“发布时间24小时内加5分”——但query千奇百怪,规则永远写不完。机器学习就是反着来:你给一堆历史数据(query、文档特征、用户点没点),让算法自己去找规律。

这里有个新手特别容易忽略的点:机器学习学的是相关性,不是因果性。有一次我给实习生看一个模型,他指着特征重要性说“这个特征权重高,说明它导致了点击”,我赶紧纠正——模型只能说“这个特征和点击相关”,至于为什么相关,得靠人去分析。把相关性当因果,是算法工程师最容易犯的错之一,尤其在做搜索排序时,很容易被特征重要性的排序带偏。

数据、特征、标签

三个最基础的词,但真要说清楚,得费点口舌。

**标签(label)**就是你想预测的东西。搜索场景里,一个候选文档被用户点击了,标签就是1,没点就是0。但这里有个坑:没点击不代表负样本。用户可能根本没看到这个文档,或者看到了但不感兴趣。所以做CTR预估时,负样本的构造是个技术活。我们组之前用“skip-above”策略——如果用户点了第5位的文档,那排在第5位之前但没被点击的文档才算负样本,排在后面的不算。这个细节直接影响模型效果。

**特征(feature)**是描述样本的维度。比如query的长度、query和文档标题的BM25相关性得分、文档的发布时间、用户的历史点击偏好等等。特征工程是搜索算法的大头,我们组有个feature store,里面几百个特征,每天还在加。

**数据(data)**就是一堆(特征,标签)的集合。这里强调一个安全意识:数据泄露。我去年做的一个模型,离线AUC冲到0.82,上线后效果稀烂。查了半天发现是特征里混进了一个“文档是否被点击过”的实时统计特征,而这个特征在训练时用的是未来信息——等于考试前偷看了答案。后来我们组立了规矩:所有特征上线前必须做时间穿越检查。

模型、损失函数、优化器

模型就是那个函数的数学形式。线性回归、逻辑回归、决策树、神经网络……不同模型有不同的假设空间和表达能力。搜索排序里,逻辑回归和GBDT是经典的baseline,深度模型(比如DNN、Transformer)是现在的主流。

损失函数衡量模型预测和真实标签之间的差距。分类问题常用交叉熵,回归问题常用MSE。选损失函数不能只看理论,得结合业务。比如做搜索排序,我们更关心排序质量而不是点击率的绝对值,所以会用pairwise或listwise的损失,而不是pointwise的交叉熵。

优化器负责更新模型参数让损失变小。SGD、Adam、AdamW……AdamW现在基本是默认选择。但有个细节:如果你用batch size很大的训练方式,AdamW的学习率要配合warmup,不然前期训练不稳定。我之前调一个DSSM双塔模型,batch size设到4096,没加warmup,loss直接NaN,查了半天才反应过来。

过拟合和正则化

这俩是入门必刷的概念。

过拟合就是模型在训练集上表现很好,在测试集上很差。原因通常是模型太复杂,把训练数据里的噪声也学进去了。搜索场景里数据量很大,但特征维度也高,过拟合依然会发生,尤其是当训练数据的时间窗口比较短(比如只用了一周的数据)时。

正则化就是给模型加约束,防止过拟合。L1正则化产生稀疏解,L2正则化让参数变小。实际中,L2用得更多,因为L1的稀疏性在深度学习中不太需要。除了L1/L2,还有Dropout、Early Stopping、数据增强这些手段。我们组做排序模型,Dropout一般设在0.1到0.3之间,设太大模型学不动,太小等于没加。

这里说个有意思的:正则化参数(比如L2的lambda)到底设多少合适?没有标准答案,只能靠验证集调。有一次我花了整整两天调一个lambda,从0.0001试到0.1,最后发现最优值是0.003。当时真的想砸键盘——花两天调一个数,性价比低得离谱。后来学聪明了,先用一个合理的范围做粗搜,再用小范围细搜。

评估指标

**准确率(Accuracy)**是最直观的指标,但在搜索排序里基本没用。因为点击率本来就低(通常1%到5%),模型全预测0,准确率也有95%以上,但没有任何意义。

AUC是排序模型最常用的指标。它衡量的是模型把正样本排在负样本前面的概率。AUC对样本不均衡不敏感,这在点击率预估里特别重要。但AUC也有局限:它只关心排序的相对顺序,不关心绝对分数。所以有时候AUC提升了,线上点击率反而没变化。

NDCG是更贴合搜索场景的指标,它考虑了位置权重——排在前面被点击,得分更高。我们组做排序模型,AUC和NDCG都会看,但最终上线决策主要参考NDCG。

AI工具在算法工作流里的位置

最近半年,组里开始推AI辅助编程。我试了Codex和Aider,说下感受。

Codex是OpenAI的代码生成模型,可以嵌在编辑器里(虽然我是Vim党,但Vim里也有插件能用)。写特征处理脚本的时候,我经常用Codex生成pandas的样板代码,比如df.groupby('query_id').apply(lambda x: ...)这种,省不少时间。但Codex生成的代码有时候会有隐藏的bug,比如变量名拼错或者边界条件没处理,必须review。安全意识在这里特别重要:AI生成的代码,默认当成实习生写的,得一行行看。

Aider是个命令行工具,直接在终端里用,和Vim天然契合。你给它一个代码仓库,用自然语言描述需求,它就能改代码、跑测试、提交commit。我在一个特征工程脚本上试过Aider,让它加一个时间窗口衰减的逻辑,它改得还挺像样,连单元测试都补了。但Aider对代码库的理解有时候会跑偏,尤其是文件多、依赖复杂的时候,改完得自己跑一遍完整的测试流程。另外,Aider调用大模型的token消耗不小,跑一次完整修改可能要花不少钱,得悠着点用。

AI搜索这块,现在很多团队在做。我们组也在探索用大模型直接生成搜索结果摘要,或者做query理解。但搜索场景对延迟要求极高,大模型的推理速度是个瓶颈。目前更多是用小模型做粗排,大模型做精排或者摘要生成,混合架构。

最后说几句

机器学习入门,概念就那么多,但每个概念背后都有业务场景里的具体含义。别只背定义,多想想“这个指标在搜索里意味着什么”“这个正则化对排序模型有什么影响”。另外,安全意识贯穿始终:数据泄露、时间穿越、AI生成代码的隐藏bug,这些都是实际工作中要时刻警惕的。

在成都做算法,节奏确实舒服。晚上加完班还能去九眼桥喝个茶,周末去青城山爬爬山。但该踩的坑一个不会少,该补的基础一个不能落。共勉吧。

评论 0

最热最新
暂无评论
孙红_程序员Lv.1
0
影响力
0
文章
0
粉丝
聊天室