ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

58同城算法工程师面试复盘:机器学习与推荐系统核心考点解析

58同城算法工程师面试复盘:机器学习与推荐系统核心考点解析 去年下半年集中面了一批算法岗58同城的算法工程师面试是印象比较深的一场。整体下来最大的感受是它不像大厂那样疯狂堆八股难度但非常看重候选人对业务的理解尤其是在本地生活服务这种供需匹配场景里算法怎么落地、怎么产生收益。这篇是“58同城2023算法工程师面试题”复盘的第二篇把能记下来的题目、考察点和我的复盘建议都写出来给准备面算法岗的朋友做一个参考。这篇文章我会尽量还原面试当天的题目类型和追问过程同时补充每道题背后的考点分析和回答思路。适合三类人看正在准备算法工程师面试的候选人、想了解58同城算法团队技术方向的同行、以及刚入行想系统梳理机器学习核心知识点的同学。1. 面试前的定向准备搞清楚58同城到底在招什么样的算法工程师1.1 从业务模式反推考察重点58同城不是传统意义上的电商或内容平台它是本地生活服务分类信息平台核心业务覆盖招聘、房产、二手车、本地服务、黄页等。这意味着它的算法场景非常集中搜索排序、推荐分发、供需匹配、风控识别、反作弊、以及部分NLP场景比如职位描述解析、房产信息结构化。这些业务特点直接决定了面试考察方向。我面下来最明显的感觉是他们不需要一个只会调参的模型工程师而是一个能理解业务、能定义问题、能把模型落地到具体场景里的人。所以面试题里频繁出现“这个场景你会怎么做特征”“这个指标怎么定义”“线上效果不好怎么排查”这类问题。面试官背景通常是推荐/搜索团队的技术骨干一面偏基础和算法题二面偏项目和业务场景三面大概率是Leader或交叉面会综合考察系统设计能力和沟通表达。整个流程对候选人的“实战感”要求很高单纯刷题背概念很难走到最后。1.2 复习优先级排序结合我的面试复盘这里的知识点重要性大概可以分成三档优先级知识点方向考察形式出现频率高机器学习基础LR、GBDT、XGBoost、特征工程概念问答公式推导必考高数据结构与算法手撕代码白板编程/在线IDE必考高项目深挖 STAR法则讲清楚来龙去脉项目问答必考中高推荐系统/搜索排序召回、粗排、精排、重排业务场景设计高频中深度学习基础DNN、Embedding、注意力机制概念推导中高频中业务指标与A/B测试评估体系搭建场景题高频低大数据工程Hive、Spark、Flink简述低频我在这里踩了一个坑前期花了很多时间刷LeetCode困难题但忽略了把LR的推导、GBDT和XGBoost的区别这类基础问题整理成“自己的话”。结果面试官一追问“XGBoost为什么用二阶泰勒展开”我回答得有点模棱两可这是非常扣分的。1.3 面试材料准备清单建议准备以下几类材料面试前一周过一遍自我介绍2分钟版本突出项目亮点和量化指标核心项目的技术方案图不一定展示但要能在脑子里画出来高频机器学习面试题熟读背诵LR推导、SVM、决策树、集成学习推荐系统黑话储备用户画像、Item Embedding、多目标优化、冷启动手写代码常考模板LRU、快排、TopK、二叉树遍历、DP几道经典题2. 算法笔试与手撕代码考察的不只是“会不会”而是“稳不稳”2.1 这轮出现的题目类型58的算法笔试和手撕代码环节难度更接近LeetCode中等题偶尔会出现一道偏工程实现的题目。这轮重点考察代码规范、边界条件处理、时间空间复杂度分析以及面试官提示时的沟通反应。我印象中涉及到的题目类型有TopK问题堆排序 / 快速选择LRU缓存实现进阶带过期时间二叉树最近公共祖先LCA最长上升子序列LIS编辑距离动态规划海量数据去重 / 统计UV类问题这些题目本身不冷门但面试现场紧张状态下容易写出“能跑但没考虑边界”的代码。我给自己定的规矩是先和面试官确认输入输出、再想时间空间复杂度、写完后主动过一遍边界用例。2.2 高频题完整拆解带过期时间的LRU缓存这题我印象很深因为面试官在常规LRU基础上加了“过期时间”这个条件。整体考察点很精准数据结构设计能力、哈希表与链表的结合使用、以及对实际缓存场景的理解。先看常规LRU的解法思路用哈希表存储 key 到节点的映射保证O(1)查找用双向链表维护访问顺序最近访问的节点移到头部容量满时淘汰尾部节点加入过期时间后需要在节点里存储过期时间戳。访问时先判断是否过期如果过期则删除并返回不存在。我现场写的核心代码逻辑是这样的class Node: def __init__(self, key, value, expire_at): self.key key self.value value self.expire_at expire_at self.prev None self.next None class LRUCache: def __init__(self, capacity): self.capacity capacity self.cache {} self.head Node(0, 0, float(inf)) self.tail Node(0, 0, float(inf)) self.head.next self.tail self.tail.prev self.head def _remove(self, node): node.prev.next node.next node.next.prev node.prev def _add_to_head(self, node): node.next self.head.next node.next.prev node self.head.next node node.prev self.head def _is_expired(self, node, now): return node.expire_at now def get(self, key, now): if key not in self.cache: return -1 node self.cache[key] if self._is_expired(node, now): self._remove(node) del self.cache[key] return -1 self._remove(node) self._add_to_head(node) return node.value def put(self, key, value, expire_at, now): if key in self.cache: node self.cache[key] node.value value node.expire_at expire_at self._remove(node) self._add_to_head(node) else: node Node(key, value, expire_at) self.cache[key] node self._add_to_head(node) if len(self.cache) self.capacity: # 注意这里要从尾部往前找第一个未过期节点淘汰 cur self.tail.prev while cur ! self.head and self._is_expired(cur, now): cur cur.prev if cur ! self.head: self._remove(cur) del self.cache[cur.key]写完后面试官追问了两个问题一是如果所有节点都过期了怎么处理二是过期节点的清理是惰性删除还是主动删除更合适。这两个问题其实很结合实际因为真实场景中还要考虑内存回收和一致性问题。我当时回答的是“惰性删除为主、定期扫描为辅”面试官认可了这个方案。2.3 手撕代码的现场经验这一环节我建议大家放慢速度不要一上来就写。先把思路说出来甚至可以在白板上画一下数据结构。面试官更看重的是你的思考过程而不是你像一个刷题机器人一样秒出答案。还有一个细节写完后一定要主动分析复杂度。我在面另一家公司的时候就是因为写完代码没有主动说复杂度被面试官提醒了一次。在58这轮我吸取了教训写完直接说时间复杂度是O(1)put和get都是空间复杂度是O(capacity)面试官点了点头就没再追问。3. 机器学习与深度学习基础那些必须“张口就来”的知识点3.1 经典模型高频考点LR、GBDT、XGBoost58这类业务场景里LR和GBDT依然是非常常用的线上模型尤其是特征规模大、需要可解释性的场景。所以面试几乎必问这几个模型。几个高频问题我梳理了一下问题一逻辑回归的损失函数为什么用交叉熵而不是均方误差这个问题的核心在于梯度。如果用均方误差损失函数对权重求梯度时会包含sigmoid函数的导数项而sigmoid在饱和区导数趋近于0导致梯度消失、收敛极慢。而交叉熵的梯度形式是预测值与真实值的差不会受sigmoid导数影响收敛更快也更稳定。我把推导过程写一下逻辑回归的预测概率为P(y1|x) 1 / (1 e^(-wx))交叉熵损失函数为L -[y * log(p) (1-y) * log(1-p)]对w求导dL/dw (p - y) * x这个梯度形式非常简洁也好解释。面试时候最好现场写出这个推导过程会比只背结论可信得多。问题二GBDT和XGBoost的区别是什么这个问题几乎是必考中的必考。我当时从几个维度回答基学习器GBDT的基学习器是CART回归树XGBoost也支持线性分类器损失函数GBDT只用到一阶导数XGBoost对损失函数做了二阶泰勒展开信息利用更充分正则项XGBoost在目标函数中加入了叶子节点数和叶子权重平方和的正则项控制模型复杂度列抽样XGBoost支持特征列抽样不仅降低过拟合风险还减少了计算量缺失值处理XGBoost能自动学习缺失值的分裂方向这里面试官可能会继续追问“二阶导数到底带来了什么好处”一定要理解到位二阶信息可以更精准地逼近真实损失函数的下降方向每一步优化的近似误差更小因此收敛更快、精度更高。3.2 深度学习与推荐模型Wide Deep、DeepFM、DIN58同城的推荐和搜索场景离不开深度学习模型尤其是点击率预估这一环。面试官考察的不是模型结构背诵而是你对模型设计动机的理解。Wide Deep的核心思想是兼顾记忆和泛化。Wide部分用LR交叉特征记住高频共现模式Deep部分用DNN自动学习低维稠密特征泛化到未出现过的组合。面试时最好举一个业务例子比如“用户最近搜索了Java开发又搜索了北京租房”Wide部分能抓住这种强相关性而Deep部分能学到“搜索行为”和“点击行为”之间的潜在关联。DeepFM相比Wide Deep的改进在于用FM替换了Wide部分让二阶特征交互不再依赖人工特征工程同时FM和Deep共享输入Embedding训练更高效。DIN则是引入了注意力机制根据候选物品自适应激活用户历史行为中相关的部分。面到这块时面试官问了我一个实战问题“用户历史行为有50个真正相关的只有2个DIN是怎么解决的”这个问题的答案就是局部激活单元对每个历史行为计算与候选物品的相关性分数再通过加权求和聚合不相关的行为权重会很小相当于做了一次软筛选。3.3 损失函数、优化器、正则化通用知识点这一部分像是“八股文”必答题但面试官会换个场景来问。比如问“点击率预估模型用什么损失函数”回答二元交叉熵再问“如果样本极度不平衡怎么办”这时候需要结合Focal Loss、负样本采样、或者训练时调整样本权重来回答。关于正则化L1和L2的区别是高频中的高频。我是这样回答的L1正则化会趋向于产生稀疏权重因为它在零点处不可导优化过程中更容易把某些权重压成0L2正则化则会让权重趋向均匀地变小不会产生严格的0值。从优化角度看L1相当于在拉普拉斯先验下做最大后验估计L2相当于在高斯先验下做最大后验估计。3.4 面试官追问示例这类基础问题最怕面试官连续追问三层。比如“过拟合怎么解决”问到后面会变成“早停机制的本质是什么”“Dropout为什么能缓解过拟合”“BN层在训练和预测时的差异是什么”。我当时被追到了Dropout训练时按概率随机丢弃神经元相当于训练了多个子网络的集成预测时保留所有神经元但权重乘以保留概率做补偿。BN层训练时用当前batch的均值和方差做归一化预测时用训练阶段累积的全局统计量。我建议准备这类问题时不要死记硬背要能讲清楚“为什么有效”。比如BN不仅仅加速收敛它还让每层输入分布相对稳定缓解了内部协变量偏移问题同时带有轻微的正则化效果。4. 业务场景与系统设计题算法工程师的“分水岭”4.1 58同城典型业务场景拆解进入二面后面试题从知识问答变成了场景设计。这一轮是最能拉开差距的地方。58同城的业务场景有几个很典型招聘场景候选人搜“Java开发”需要展示职位列表这时候搜索排序的目标不只是点击率还要考虑求职者和职位的双向匹配度房产场景用户浏览二手房信息流需要判断用户是在找房还是随便看看决策周期长、客单价高不能只看短期点击二手车场景车源信息结构化程度低标题和描述里的噪音多需要从文本里抽品牌、车型、年份、里程等关键属性本地服务场景供需两端都是中小商家冷启动问题非常严重面试官给了一道比较开放的题假设要在58同城的招聘场景里做一个推荐系统从0到1怎么设计这类题没有标准答案但有一个通用的回答框架。4.2 推荐系统设计题的回答框架我大概是按“漏斗”结构来回答的召回 - 粗排 - 精排 - 重排。召回阶段要保证召回率和多样性。我当时给出的方案是多路召回包括基于用户实时行为的协同过滤看了A职位推荐相似职位基于画像的召回用户是3年Java经验推荐3-5年经验的Java岗位热门召回当前城市高热度职位地理位置召回2公里内的职位优先语义召回用户的搜索词和职位文本做向量召回粗排阶段要用轻量模型对召回结果做打分把候选集从几千降到几百。我建议用双塔模型塔两侧分别是用户特征和职位特征线上可以用向量内积快速计算性能压力小。精排阶段用DeepFM或DIN这类模型输入特征包括用户侧特征、职位侧特征、交叉特征、上下文特征目标函数可以做多目标比如同时优化点击率、收藏率、投递率。重排阶段更多考虑业务规则和多样性同一家公司的职位不能连续出3个、同类职位不能刷屏、需要插入广告位或运营位。面到这一步面试官基本可以判断你是有真实推荐系统经验还是只会背八股。所以我在回答时每讲一个模块都会加一句“在我们实际项目中这里会遇到什么问题”。比如向量召回时Item的Embedding更新频率不能太高否则会导致线上特征分布不稳定。4.3 冷启动、数据稀疏、实时性挑战业务场景问答里冷启动几乎是必考项。58同城的冷启动有两面新用户的冷启动和新职位的冷启动。新用户的冷启动我的方案是分阶段处理冷启动初期用地理位置、手机品牌、设备信息等基础画像兜底用户产生行为后立即用实时行为更新推荐结果比如用户点击了“行政专员”立刻推荐同类别职位行为量积累到一定程度后切换为常规推荐模型新职位的冷启动核心是解决“没有历史行为数据”的问题。我当时提出了两个思路一是利用职位文本信息做内容相似度匹配比如“招聘Java开发”和“招聘Java工程师”在语义空间上应该很近二是利用商家的历史职位效果做迁移如果这家公司之前发布的“销售”职位效果很好新发布的“销售主管”可以继承一部分初始权重。面试官还追问了数据稀疏问题。这里要提到Embedding训练时的频率截断低频特征如果不处理会让Embedding矩阵过度膨胀且学不到有效信息。一个常用做法是对出现次数小于阈值的特征做哈希压缩或直接归一到“未知”桶中。另外对长时间无曝光或无点击的Item要降权或做下架处理否则会占用大量线上存储且影响推荐质量。4.4 排序模型特征体系与评估指标特征体系怎么搭是面试官很爱细挖的点。我在回答时是这么分类的特征类别示例使用要点用户侧特征城市、年龄、会员等级、历史行为序列注意用户特征时效性Item侧特征职位薪资、学历要求、发布时间、公司规模缺失值要单独处理交叉特征用户城市-职位城市、用户技能-职位技能两两组合效率低用FM类模型替代实时特征用户最近5分钟点击次数、职位最近1小时曝光点击率注意特征一致性上下文特征当前时间、星期几、设备类型容易被忽视但影响显著评估指标的选择也要分业务目标来说。如果是一个投递率优化项目线上指标可以看投递率、投递转化率、以及商家的完聘率线下离线指标可以选择AUC和GAUC。但这里必须补充一点AUC高不代表线上效果好因为AUC衡量的是排序能力而不是绝对校准度所以需要同时关注LogLoss或者使用Calibration指标。还有一个重要的问题是A/B测试。面试官问我“新模型上线转化率提升了1%你会怎么判断这个结果是否可信”。我的回答是看样本量是否足够、是否达到统计显著性、分流是否均匀、实验周期是否覆盖了周末和工作日、以及有没有其他因素干扰比如同时上线了运营活动。这一串讲下来面试官比较满意因为说明我有真实上线经验。5. 项目深挖与简历面试面试官到底想听什么5.1 STAR法则讲项目到了三面左右面试官手里拿着你的简历问得最多的就是“讲一个你认为最有代表性的项目”。这时候千万不要从技术栈开始介绍而是用STAR法则先把问题背景讲清楚。我当时讲的是一个信息流推荐排序优化项目。我是这样组织的背景Situation信息流场景点击率连续三周下跌需要定位原因并优化任务Task提升整体点击率5个百分点同时控制时长类指标不下跌行动Action从三个维度排查召回侧的多样性、精排侧的模型更新频率、重排侧的频控策略结果Result通过增加实时特征和优化样本权重点击率提升了6.2%人均点击次数稳定这不是一个多复杂的项目但因为结构清晰面试官很容易跟着我的思路走。如果上来就讲“我用DeepFM替换了LR”面试官根本没法判断你的思考质量。5.2 面试官最喜欢追问的四个问题项目讲完之后面试官的追问基本集中在四个方向第一个是“为什么选这个方案”。我当时用了DeepFM而不是DIN原因是DIN的注意力机制虽然表达能力强但需要足够的用户行为序列数据支撑在冷启动占比高的场景里收益不稳定而DeepFM结构更简单、线上推理更快、上线风险更低。第二个是“有没有做过对比实验”。这个问题考察的是工程严谨性。我当时的对比实验包括LR基线、DeepFM、以及DeepFM实时特征三组在相同训练数据和评估集下比较GAUC。如果没有对比实验建议老实说没做然后补一句“如果重做我会怎么做对比”。第三个是“如果重新做这个项目你会改变什么”。这个问题非常考察复盘能力。我当时说的是样本设计上可以做得更细比如对负样本做更精细的采样而不是简单随机采样另外在特征更新频率上可以增加瀑布流式更新避免全量重训带来的资源浪费。第四个是“线上效果不好你会怎么排查”。这个问题的标准思路是先确定指标口径是否一致再分层排查——数据层、特征层、模型层、策略层。数据层看样本是否有泄漏和延迟特征层看线上特征是否和训练时一致模型层看指标是否正常变化策略层看是否被其他策略压制。5.3 简历上的每个数字都要能讲出故事简历上写了“点击率提升10%”面试官一定会问“怎么算出来的”“口径是什么”“实验跑多久了”“提升的是相对值还是绝对值”。我见过很多候选人被问到这里就含糊了非常可惜。我的建议是简历上每个量化结果后面暗暗准备三层解释。第一层是计算方法比如基于A/B实验实验组点击率5.2%对照组4.7%相对提升10.6%第二层是实验条件跑了两周样本量单组约50万置信度95%第三层是是否有其他因素干扰同期没有上线其他策略运营活动已剔除。这样准备之后面试官追问任何一层都能接住。6. 面试复盘与高频问题速查表6.1 整场面试流程复盘58同城的算法工程师面试流程大概是简历筛选 - 笔试/在线评估 - 技术一面 - 技术二面 - Leader面/交叉面 - HR面。一面重点在算法题和机器学习基础时长约60分钟二面重点在项目深挖和场景设计时长约60-75分钟三面更偏向综合能力和业务思维也会涉及一些软素质问题比如“你如何推动一个跨团队的项目落地”。我给自己的复盘结论是一面看基本功扎不扎实二面看实战经验深不深三面看沟通和推动力。所以准备的时候不要只刷题要把项目经历、业务理解和技术方案全部串成一条线。6.2 高频问题速查表类别高频问题回答要点算法题实现LRU缓存哈希表双向链表O(1)操作算法题TopK问题堆O(NlogK)快速选择O(N)机器学习LR和SVM的区别损失函数、概率输出、边界特性机器学习GBDT和XGBoost区别二阶导、正则项、列抽样、缺失值深度学习BN的作用归一化、加速收敛、轻微正则化深度学习为什么用ReLU缓解梯度消失、计算简单、稀疏激活推荐系统召回和排序的差异召回保覆盖率排序保精度推荐系统冷启动怎么解决内容偏好、热门兜底、渐进式探索业务题新模型上线怎么评估离线指标在线A/B显著性检验业务题线上效果变差怎么排查数据层-特征层-模型层-策略层6.3 一些独家建议最后分享几个我在实际面试中踩过的坑和验证过有效的经验。第一个坑是不要只准备模型细节却答不好“这个特征为什么要做分桶”。58的面试官非常关注特征工程的实际落地因为他们线上场景里很多特征就是离散化后用LR或GBDT处理的。我建议把常用的分桶方法等距、等频、卡方分桶、基于目标编码的分桶都过一遍。第二个经验是面试时讲概率推导不要跳步也不要直接给结论。比如“LR为什么用交叉熵”这个问题最好从MSE会造成梯度消失开始一步一步推导到交叉熵梯度的简洁形式。这样即使你最终没完全推完面试官也会因为你逻辑清晰而给不错的评价。第三个经验是如果要面业务场景题建议提前准备一个“自己最熟的场景”反复打磨回答。比如你可以选“招聘职位的搜索排序”把所有可能的问题都往这个场景上靠。这样做的好处是不管面试官怎么变化你都能用同一套深度打磨过的内容来回答会比临场组织语言流畅得多。
返回列表