多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

算法工程师笔试备战:机器学习与数据结构核心考点全拆解

算法工程师笔试备战:机器学习与数据结构核心考点全拆解 1. 这场笔试在考什么网易2018校招算法岗的考察逻辑2018年那会儿正是各大厂校招算法岗最卷的起点网易这套机器学习算法工程师笔试卷放在今天看仍然有很强的参考价值。整套题覆盖了机器学习基础理论、经典算法推导、数据结构与手撕代码、概率统计与最优化方法四大板块题型以选择题、填空题、简答题和编程题混合为主考试时间通常在90到120分钟之间题量不算小想要从容做完需要平时积累足够扎实。很多准备校招的同学有一个误区以为算法工程师笔试主要考深度学习结果一上来看到朴素贝叶斯、KMP、快速幂这些题目就懵了。实际上面向校招的算法岗笔试卷考察的核心并不是“你追了多新的SOTA模型”而是“你的机器学习基本功是否成体系、代码能力是否过关、数学推导是否熟练”。网易这套卷子很好地体现了这一点它不会追热点反而会反复考察那些最基础、最容易被忽视的知识点。从能力模型的维度拆解这套笔试卷至少考察了四层能力机器学习理论功底偏差方差分解、过拟合处理、SVM原理、决策树分裂准则、聚类算法、PCA降维。数据结构与算法编码能力KMP的next数组、排序算法的稳定性与复杂度、贪心与动态规划、图论最短路。数学与概率基础贝叶斯公式、期望与方差、极大似然估计、矩阵特征值与SVD、梯度下降的收敛性。工程思维与代码规范编程题的时间复杂度、边界条件、代码可读性。我当年在准备这类笔试时最大的感受是每个知识点都不能只停留在“能背出概念”的程度而是要能手推、能手写、能讲清楚为什么。网易的题目尤其喜欢在选项里埋一些“看起来对、实际错”的细节如果只是死记硬背很容易被绕进去。接下来我就按这套卷子最常出现的考点逐一做拆解。2. 机器学习核心考点过拟合、经典模型与集成学习的真题拆解2.1 过拟合的判断题偏差方差分解是送分题还是送命题过拟合几乎是每一套机器学习笔试题的必考项。网易这道题通常这样出给定一个模型的训练误差和验证误差表现问当前模型处于高偏差还是高方差状态应该用哪种手段来缓解。选项里会出现增加训练数据、降低模型复杂度、增加正则化系数、换更复杂的模型等。这里的关键不是死记“高方差就加数据、加正则”这个结论而是理解偏差方差分解的内在逻辑。模型的总误差可以分解为偏差的平方、方差和不可约噪声三项。偏差衡量模型的预测期望与真实结果的差距高偏差意味着模型太简单拟合能力不足典型表现是训练误差和验证误差都很高方差衡量模型对训练集波动敏感的程度上高方差意味着模型过拟合了训练集中的噪声典型表现是训练误差很低、验证误差明显更高。我记得这套卷子里有一道很有迷惑性的题模型在训练集上误差很低在测试集上误差较高问是否可以增加训练样本数来解决问题。如果只是机械地认为“过拟合就加数据”很容易选对但出题人可能把问题改成“训练集数量已经很大再增加数据收益很小此时更好的做法是什么”答案就转向了正则化、特征筛选或集成方法。所以复习时一定要把偏差方差分解的公式、图形曲线、每种缓解手段的作用机制串成一条线。另一个容易丢分的点是正则化的细节。L1正则化会带来稀疏解因为它在零点不可导优化过程中更容易把系数压到零L2正则化只会让系数整体收缩不会产生精确的零。题目如果问“L1和L2的区别”除了提到稀疏性还可以补充一句L1正则化等价于对参数引入拉普拉斯先验L2正则化等价于引入高斯先验。这句话在简答题里是加分项。2.2 经典模型原理题SVM、决策树与朴素贝叶斯网易笔试对经典模型的考察非常细。SVM部分常考的点包括支持向量的定义、间隔最大化的含义、软间隔中的松弛变量、核函数的选择条件。这里有一个高频考点核函数需要满足什么条件。答案不是“能映射到高维空间就行”而是必须满足Mercer条件即核矩阵是半正定的。很多同学把“核技巧能避免显式计算高维内积”和“核函数的合法性条件”混在一起后者才是考点。决策树在这个阶段是必考的。围绕ID3、C4.5和CART三种算法要清楚每个算法的分裂准则是什么ID3使用信息增益倾向于选择取值较多的特征容易过拟合。C4.5使用信息增益比对取值多的特征做了惩罚但计算时需要先算分裂信息。CART使用基尼系数既可以做分类也可以做回归且生成的是二叉树。真题里常见的一种考法给出一个包含若干样本的小数据集手动计算某个特征的信息增益判断决策树会如何分裂。这种题考察的是对信息熵公式的掌握程度平时不动手算过几遍考场上很容易在log运算上卡壳。我建议复习时拿周志华《机器学习》第四章的例子手推一遍把熵、条件熵、信息增益、基尼系数的计算流程吃透。朴素贝叶斯也是必考内容。除了基础的贝叶斯公式计算更需要注意的是“朴素”二字的含义——它假设特征之间相互独立。这个假设在现实中往往不成立但为什么朴素贝叶斯在实践中仍然有效有一个常见的解释是即使特征不独立只要各特征的条件概率估计不发生严重偏差分类边界仍然可以是接近最优的。此外要知道朴素贝叶斯有三种常用的概率估计方式多项式模型、伯努利模型、高斯模型分别适用于离散计数特征、布尔特征和连续特征。2.3 聚类、降维与集成学习锦上添花还是雪中送炭聚类算法在网易这类大厂笔试中基本不会缺席。K-means的高频考点有K值如何选择肘部法则、使用欧氏距离的局限对尺度敏感、初始化敏感与K-means、与高斯混合模型GMM的关系K-means是GMM在协方差为单位阵且每个簇权重相等时的特例。如果出简答题很可能让你说明K-means的收敛性保证——目标函数是簇内平方和算法在两步迭代中单调不增因此一定能收敛但可能收敛到局部最优。降维部分绕不开PCA。除了要会背“PCA通过最大化投影后方差或最小化重构误差来找到主方向”之外还要理解它的计算流程数据中心化→计算协方差矩阵→特征值分解→按特征值大小选取主成分。这里有一个易错点PCA是无监督方法它不利用样本标签而LDA线性判别分析是有监督降维目标是最大化类间距离与类内距离的比值。笔试题特别喜欢把这两个放在一起对比。集成学习几乎是必考综合题。Bagging和Boosting的区别要信手拈来Bagging对样本进行有放回抽样训练多个基学习器后投票或平均能显著降低方差典型代表是随机森林。Boosting是串行训练一系列弱学习器每个学习器重点关注前一个学习器犯错的样本最终加权组合主要降低偏差典型代表是AdaBoost和GBDT。随机森林在Bagging的基础上还引入了特征随机选择相当于在样本扰动之外又增加了特征扰动进一步降低了树之间的相关性。这套卷子里如果出现“随机森林中为什么要对特征进行随机抽样”这种题答案不是“为了降低计算量”而是“为了降低各棵树之间的相关性”。如果树之间高度相关投票的多样性就没了Bagging降低方差的效果会大打折扣。这个点经常被误解值得专门记一笔。3. 数据结构与手撕算法KMP的next数组、排序与图论高频题3.1 KMP的next数组看起来简单算错的人一大片KMP算法是笔试中字符串匹配的经典考点网易的题目常常直接给出一个模式串比如pabacaba要求写出它的next数组。这里考察的不是你会不会背KMP框架而是你是否真的理解next数组的定义和计算方法。先统一约定一种常见定义方式next[i]表示模式串p[0...i]这个子串中最长相等前后缀的长度不包含子串本身。对pabacaba逐位计算i0子串a没有真前后缀next[0]0。i1子串ab前缀a后缀b不相等next[1]0。i2子串aba前缀a后缀a长度为1前缀ab后缀ba不匹配所以next[2]1。i3子串abac长度为1时a和c不等next[3]0。i4子串abaca前缀a等于后缀a长度为1长度2时ab与ca不等next[4]1。i5子串abacab长度1的a与b不等长度2时ab与ab相等next[5]2。i6子串abacaba长度1时a等于a长度2时ab与ba不等长度3时aba与aba相等next[6]3。所以next数组为[0, 0, 0, 1, 0, 1, 2, 3]如果包含next[0]作为空串的哨兵位则会额外在前面补一个-1不同教材定义略有差异看题时要先确认题目给的定义。这道题真正的难点在于很多同学会用“失配时跳到哪”这个视角去理解next数组导致计算出来的逻辑是错的。我自己的体会是把next数组理解成“最长公共前后缀长度表”然后配套记忆KMP的失配跳转逻辑两者不要混在一起考试时才不容易出错。3.2 排序算法全家桶复杂度、稳定性与手写快排排序算法在笔试里属于“必考但不能只背结论”的部分。常考形式有三种选择题判断某个排序算法的稳定性与时间复杂度、填空题写出一趟排序后的序列状态、编程题手写快排或归并排序。对于稳定性很多同学靠死记这里分享一个实用的判断思路如果排序过程中相同元素的相对位置可能被交换那算法就是不稳定的。选择排序不稳定因为每次选择最小值与当前位置交换时可能把相同元素的顺序打乱快排不稳定因为partition过程中会跨越式交换堆排序不稳定因为堆调整的过程会改变相同元素的相对顺序。稳定算法典型代表是冒泡、插入、归并。网易这套卷子我印象比较深的一道题是给出初始序列要求写出堆排序构建初始大顶堆后的结果。这种题考察的不是代码能力而是对堆调整过程的理解。做题方法是先按完全二叉树的顺序结构把序列画成树然后从最后一个非叶子节点开始自底向上执行下沉操作。这里有个小技巧最后一个非叶子节点的下标是floor(n/2)-10基下标很多同学下标算错导致整体调整全错。如果编程题要求手写快排建议写一个简洁的递归版本注意partition的边界条件。我常用的写法是Lomuto分区def quick_sort(arr, left, right): if left right: return pivot arr[right] i left for j in range(left, right): if arr[j] pivot: arr[i], arr[j] arr[j], arr[i] i 1 arr[i], arr[right] arr[right], arr[i] quick_sort(arr, left, i - 1) quick_sort(arr, i 1, right)这种写法在面试白板编程中不容易出错但要注意它每次选取最后一个元素作为pivot对基本有序的数组会产生最坏时间复杂度。如果面试官追问优化思路可以回答随机选pivot、三数取中、在递归深度过大时切换到插入排序。3.3 图论与优化算法Dijkstra、快速幂与贪心/动态规划图论算法常见的考法是概念题加一道中等难度的编程题。Dijkstra算法常考它的适用条件——边权必须非负以及它的时间复杂度。如果用朴素数组实现复杂度是O(V²)适合稠密图如果用小根堆实现复杂度降到O((VE)logV)适合稀疏图。网易的题有时候会给一张带权图让你模拟运行Dijkstra算法的前几步这要求熟悉“每次从未确定最短路的节点中选距离最小的节点”这个过程。快速幂也是笔试中的高频题特别是考察大数取模的时候。核心思想就是把指数二进制展开将乘法次数从O(n)降到O(logn)。最基本的实现如下def fast_pow(base, exp, mod): res 1 base % mod while exp 0: if exp 1: res (res * base) % mod base (base * base) % mod exp 1 return res笔试里快速幂常常和“计算某个数的n次方后取模”一起出现直接递推累乘在指数较大时会超时快速幂是标准解法。简答题如果问原理关键是按位判断当前二进制位是0还是1决定是否乘入结果。动态规划和贪心的区别也常被考察。简答形式通常是给一个场景判断用什么算法并说明理由。贪心算法每一步都选择当前看起来最优的方案不回溯动态规划会记录子问题的解并考虑状态转移。贪心必须证明贪心选择性质动态规划则需要问题具有最优子结构和重叠子问题。网易笔试题里出现过“找零钱问题能否用贪心”这种讨论——当硬币面额不满足特定规律时贪心不一定得到最优解必须用动态规划。4. 数学基础与概率统计贝叶斯、极大似然与矩阵优化的笔试题4.1 贝叶斯公式的应用题先验、似然和后验别搞混概率统计是机器学习算法工程师笔试中区分度最高的部分。网易这套卷子里有一道很典型的贝叶斯计算题某种疾病在人群中的发病率为0.001检测试剂的灵敏度真正率为0.99特异度真负率为0.95问一个人检测结果为阳性时真正患病的概率是多少。解法就是直接套贝叶斯公式。设P(D) 0.001P(T|D) 0.99P(T|¬D) 0.05那么P(D|T) P(T|D)P(D) / [P(T|D)P(D) P(T|¬D)P(¬D)]代入计算P(D|T) 0.99×0.001 / (0.99×0.001 0.05×0.999) ≈ 0.001 / (0.001 0.04995) ≈ 0.0194。也就是阳性结果下真正患病的概率不到2%。这个结果非常反直觉所以贝叶斯相关题目经常作为选择题出现四个选项里有一个就是“检测灵敏度很高所以阳性基本等于患病”。这就是对先验概率不够敏感的典型错误。复习时一定要记住后验概率不仅取决于似然还取决于先验。当先验概率极低时即使检测很准阳性结果也不一定意味着患病。4.2 极大似然估计与常见分布简答题的得分点极大似然估计是机器学习理论题里的常客。网易出现过一道很经典的简答题假设样本独立同分布于正态分布N(μ, σ²)请用极大似然估计给出μ和σ²的估计值。解题步骤非常固定先写出似然函数取对数后分别对μ和σ²求偏导并令其为零。最终得到μ的估计是样本均值σ²的估计是样本方差但要注意分母是n而不是n-1。这说明极大似然估计的方差是有偏的而无偏估计量会使用n-1作分母。题目如果继续追问“为什么机器学习中经常用极大似然估计”可以回答它提供了一套统一的参数估计框架并且很多损失函数可以看成负对数似然比如交叉熵对应分类问题中的多项分布似然均方误差对应高斯分布似然。除了正态分布还要熟悉伯努利分布、二项分布、多项分布、泊松分布、指数分布这些常见分布的概率密度/质量函数和期望方差。尤其是指数族分布的概念因为逻辑回归、线性回归、Softmax回归都可以统一到广义线性模型框架下。这个观点如果能在简答题中写出来会比较加分。4.3 矩阵特征值、SVD与梯度下降线性代数的考察重点是特征值分解、奇异值分解和正定矩阵。PCA的数学基础就是协方差矩阵的特征值分解推荐系统中的矩阵分解、潜在语义分析等则常常用到SVD。常见考点包括SVD中奇异值与特征值的关系对于实对称矩阵奇异值等于特征值的绝对值、SVD能用于矩阵的低秩近似通过截断保留最大的k个奇异值。梯度下降相关题目通常考察学习率的影响、随机梯度下降和批量梯度下降的权衡、以及损失函数等高线图。网易出现过“在某个等高线图上画出梯度下降的轨迹”这种题实际上考察的是梯度方向是等高线的法线方向学习率过大时会在山谷两侧震荡学习率过小时收敛速度极慢。遇到这类题目可以记住一条结论对于条件数很大的损失函数标准梯度下降收敛很慢此时可以考虑动量法、AdaGrad、RMSProp或Adam等自适应方法。这个结论在面试中被追问的概率也很大值得准备一段话术。5. 编程实战从逻辑回归到K-means的手写代码题5.1 手写逻辑回归考察的是工程细节而不是模型本身网易的笔试卷通常会有一道或两道算法编程题。有一类编程题是“不调库实现一个逻辑回归模型的训练过程”。很多人觉得模型简单但真正白板写的时候会暴露很多问题参数初始化怎么设、梯度更新公式有没有写对、要不要加正则项、训练轮数或停止条件怎么定。逻辑回归的核心就是sigmoid函数加交叉熵损失。梯度更新用标准的批量下降或随机下降都可以。一个常见的写法如下import numpy as np def sigmoid(z): return 1 / (1 np.exp(-z)) def train_lr(X, y, lr0.1, epochs100, lam0.01): m, n X.shape w np.zeros(n) b 0.0 for epoch in range(epochs): z np.dot(X, w) b a sigmoid(z) dw np.dot(X.T, (a - y)) / m lam * w db np.sum(a - y) / m w - lr * dw b - lr * db return w, b这段代码里有两个细节值得说明。第一梯度计算里加正则项时L2正则对w求导是2λw但很多实现会取λw并把2倍吸收到λ里需要在笔试中说明清楚避免和面试官对不上。第二sigmoid函数在z绝对值很大时可能出现数值溢出更稳妥的写法是分段计算或者用scipy.special.expit中的方式处理。不过在白板题中把公式写对、流程写清楚是第一位的。5.2 手写K-means边界条件和初始化策略一个都不能少另一道常见编程题是手写K-means聚类。这道题看着简单写完并不容易因为要注意的细节很多初始中心怎么选、空簇怎么处理、收敛条件是什么、最大迭代次数怎么设置。一个基础版本如下import numpy as np def kmeans(X, k, max_iters100, tol1e-4): # 随机选k个样本作为初始中心 indices np.random.choice(len(X), k, replaceFalse) centers X[indices] for _ in range(max_iters): # 分配样本到最近中心 distances np.linalg.norm(X[:, None, :] - centers[None, :, :], axis2) labels np.argmin(distances, axis1) # 更新中心 new_centers np.array([X[labels j].mean(axis0) if np.sum(labels j) 0 else centers[j] for j in range(k)]) if np.linalg.norm(new_centers - centers) tol: break centers new_centers return labels, centers这里我用了一个技巧当某个簇为空时保留原来的中心而不是直接报错这样算法可以继续跑下去。但在实际面试中更推荐的方案是重新随机初始化一个中心比如从样本中随机挑一个点替代空簇中心。另外如果笔试环境不允许使用numpy需要写出纯Python版本的向量距离计算和均值更新逻辑要完整时间复杂度可以不是最优。手写K-means的题目通常还会追加一个小问K值如何确定。考察的就是肘部法则和轮廓系数这两个概念把“簇内误差平方和随K的变化趋势”以及“轮廓系数的取值范围在-1到1之间越大越好”这两点写清楚就行。5.3 编程题的答题策略暴力解先写优化再谈网易的编程题是ACM风格还是传统笔试题风格每年略有浮动。但整体上校招笔试的编程题难度处于LeetCode中等偏简单到中等水平。笔试时间有限我的建议是先把所有题目快速扫一遍标出哪些是送分题哪些需要思考。编程题如果一时想不出最优解先写一个暴力解的完整框架保证拿到部分分数。写代码时把变量命名写清楚函数逻辑分层不要全部挤在一个函数里方便自己调试也方便阅卷人看。注意Python的输入输出模板有些笔试平台输入格式比较坑字符串和数字混着来处理不好会浪费大量时间。6. 备考经验与高频踩坑点按这套思路复习最稳6.1 复习资料怎么搭配教材、公开课、刷题缺一不可如果你现在准备的是机器学习算法岗我比较推荐的复习路径是以周志华《机器学习》西瓜书为教材主线搭配吴恩达的机器学习课程作为入门辅助然后用李航《统计学习方法》补充SVM、最大熵模型、EM算法等进阶内容。吴恩达的课程对数学基础要求不高适合建立直觉西瓜书适合系统性的理论和公式推导李航的书公式密度大适合在面试前一到两周做快速回顾。刷题方面LeetCode按标签刷重点刷数组、链表、二叉树、哈希表、动态规划、贪心、图论这几类。网易这类大厂笔试不太会出特别偏的题但动态规划和贪心是高频考点要确保能做出来中等的题目。6.2 我踩过的坑别只背结论要能手推公式我自己在准备过程中最大的教训是很多知识点“看着会、一推就废”。比如SVM的对偶问题推导、PCA的拉格朗日乘子法求解、逻辑回归梯度公式的来源这些如果只看别人整理好的笔记考场上遇到推导题照样卡壳。后来我强制自己每一章结束后合上书在纸上把关键公式重新推一遍推不出来的地方标记出来重点复习。这个方法虽然慢但效果非常明显。另一个容易被忽略的是手写代码能力。平时看题解都能看懂但真正上了笔试平台脱离了IDE的自动补全很多人连快排都写不利索。我在考前两周每天固定手写三到五个经典算法包括KMP、快排、堆排、Dijkstra、快速幂、二分查找全部在纯文本环境里写不依赖任何提示连续练了十天手感和速度明显提升。6.3 考点优先级哪些值得投入大量时间哪些战略放弃以网易2018这套题为参考机器学习算法工程师笔试的考点优先级可以这样排最重要的经典机器学习模型原理与推导、偏差方差与过拟合、常见损失函数与优化方法、数据结构与手撕算法基础题。这些是必考且出现频率最高的。次要但常考的聚类与降维、集成学习、贝叶斯与概率计算、PCA与SVD、动态规划/贪心中等题。优先级靠后的深度学习架构细节、最新论文模型、大规模分布式训练、强化学习、粒子群/模拟退火等启发式算法细节。这些内容可能在面试中被问到但笔试中出现概率不高。我当时在复习粒子群算法和模拟退火这类优化算法时只花了较少时间因为网易这套卷子不会考到具体的粒子速度更新公式最多是选择题里问一句“哪种算法属于启发式优化算法”。反倒是KMP和快排这种基础算法经常以填空或编程题的形式出现。把时间花在最核心的部分性价比最高。6.4 笔试现场的时间分配先拿稳拿分再攻坚难题网易的笔试题型较多时间管理非常关键。我的经验是拿到试卷后先花两到三分钟浏览全部题目标记出哪些是概念选择题、哪些是数学推导题、哪些是编程题。然后优先做自己能快速确定的题比如判断题、简单选择题、直接套公式的概率题。编程题如果一道卡了超过十五分钟还没思路先放下把后面能拿的分拿完后回来再想。选择题里有一种情况很常见四个选项中有两个明显错误剩下两个需要精确计算或仔细辨析。这时候不要凭感觉尽量用排除法和代入法把能确定的错误选项先划掉再在剩余选项中比较。如果是涉及数字计算的选择题我会直接在草稿纸上写计算过程而不是心算因为心算出错的概率太高。最后说一句我在准备和参加这类笔试时最深的体会算法工程师笔试考的不是你会多少模型而是在有限时间内稳定输出基本功的能力。这种能力没有任何捷径只能靠反复的推导、手写、刷题来磨。希望这篇拆解能帮你梳理清楚复习的重点和顺序少走一些我当初走过的弯路。
返回列表