2023年网易提前批深度学习算法工程师的笔试,我是在线上完成的。整套题做完最大的感受是:网易的笔试不是单纯考你“会不会调模型”,而是考你在有限时间内能不能把基础概念算清楚、把代码写利索。整个笔试大概三个小时,内容覆盖了深度学习基础、机器学习基础、数据结构与算法,还有几道偏工程实现的题目。这篇复盘我尽量按考试时的真实体验来写,包括题型分布、重点考点、我当时的解题思路,以及考完以后对完答案才明白的坑,希望对后面准备大厂算法岗笔试的朋友有价值。
先说结论:网易这套题整体难度中等偏上,但在选择题和填空题上挖了不少“概念细节”的坑。比如Float32和Float16在深度学习训练中的区别、CNN感受野的具体计算、KMP算法中next数组的手算结果,这些题目在刷LeetCode时很少碰到,却在网易的卷子里占了不少分值。如果你也想走深度学习算法工程师这条路,建议除了刷题,也要把深度学习框架底层的浮点数规则、卷积计算细节都过一遍。
1. 笔试整体复盘:今年的题型和节奏
1.1 熟悉网易笔试的风格和题型分布
网易2023校招提前批笔试和往年一样,采用的是在线编程系统,题目结构大概分三块:单选题、多选题、编程题。单选和多选覆盖机器学习、深度学习、算法与数据结构,编程题则是两到三道算法题,需要在规定时间内提交,支持C++、Java、Python等主流语言。
从题型权重来看,深度学习和机器学习相关的选择题占了将近一半的分值,剩下的数据结构和算法又占了三成左右,编程题虽然只有两三道,但单题分值很高,所以编程题能不能AC,直接决定你能不能进下一轮面试。今年我印象最深的是选择题里出现了不少“工程向”的题目,比如混合精度训练时不同浮点格式的取舍,这在大厂笔试中越来越常见,但很多同学在准备时只盯着Transformer和卷积网络,很容易把这类基础题漏掉。
1.2 提前批与正式批的差异
提前批笔试和正式批最大的区别在于:提前批更看重候选人的全面性。我当时对比过身边同学参加正式批的回忆版题目,发现正式批的笔试似乎更偏向于深度学习模型本身,比如对Transformer和CNN结构的理解,而提前批则明显加入了大量算法题和机器学习基础题。这可能意味着在提前批阶段,面试官更希望筛选出工程基础扎实、综合能力强的候选人。
另一个区别是时间节奏。提前批笔试通常安排在投递简历后一到两周,而且很多部门是“先到先得”,你笔试成绩出来得越早,面试官约面就越早。所以如果你准备投提前批,最好在投递之前就把数据结构刷过一轮,不要等系统发了笔试通知才开始准备,那样大概率会手忙脚乱。
1.3 我当时的准备思路与复盘框架
我的准备周期大约是四周,前两周把机器学习经典算法和深度学习基础理论过了一遍,第三周主攻LeetCode热题HOT 100和剑指Offer,第四周刷了大概近两年的网易笔试回忆题和牛客网上的模拟卷。考前最后两天没有再做新题,而是把所有错题重新整理了一遍,尤其是浮点数、卷积计算、排序稳定性这类容易混淆的细节点。
这份复盘我会按题型逐一展开,选择题部分挑那些“考完才后悔没多看一眼”的题目来细讲,编程题部分则把我考场上的解法和赛后优化思路一起写出来,包括复杂度分析、边界条件处理,以及一些笔试环境里容易踩的输入输出坑。
2. 核心考点拆解:深度学习基础题才是胜负手
2.1 卷积计算与感受野:一道题考出基本功
考场上有道选择题大概是这样的:一个输入特征图尺寸是 224×224×3,第一层卷积用 16 个 7×7 的卷积核,stride=2,padding=3,问输出特征图的尺寸是多少。标准公式是:
[ output = \lfloor \frac{input + 2 \times padding - kernel}{stride} \rfloor + 1 ]
代入的话是:
[ \lfloor \frac{224 + 2 \times 3 - 7}{2} \rfloor + 1 = \lfloor \frac{223}{2} \rfloor + 1 = 111 + 1 = 112 ]
所以输出是 112×112×16。这道题本身不难,但有两个容易粗心的地方:一是计算时忘记把padding乘以2,很多人直接算成 (224-7)/2+1,得到大约108.5,再取整就错了。二是把stride=2漏掉,直接从224算出218,导致选项对不上。
接着还有一道延伸题,问两层卷积串联之后的感受野。假设第一层是3×3卷积,stride=1,第二层还是3×3卷积,stride=1,那么第二层输出特征图上的一个点,实际上对应原始输入上的多大区域?第一层感受野是3,第二层在第一层的基础上再往外扩,计算公式为:
[ RF_2 = RF_1 + (kernel_2 - 1) \times stride_accumulate ]
由于每一层的stride都是1,所以:
[ RF_2 = 3 + (3-1) \times 1 = 5 ]
也就是说,两层3×3卷积串联后的有效感受野是5×5,这和一层5×5卷积在感受野上是等价的,但参数量更少。这个知识在面试里也经常被问到,笔试里考一次非常合理。
2.2 损失函数与优化器:必考的对比题
网易这份卷子里,损失函数和优化器一共出了至少四道题,这个密度相当高。有一道多选题问的是“关于交叉熵损失和均方误差损失的说法,哪些是正确的”,选项包括:交叉熵损失常用于分类任务,均方误差对异常值更敏感,Softmax与交叉熵结合时梯度形式更简洁,以及MSE在分类任务中会导致梯度更新过慢。这几个选项其实全对。
我重点说第三个选项。很多人不理解为什么分类任务推荐“Softmax + 交叉熵损失”而不是“Softmax + MSE”。我们可以推导一下:假设网络输出经过Softmax后得到概率 ( p ),真实标签是 one-hot 向量 ( y )。交叉熵对 Softmax 输入的梯度算下来是 ( p - y ),这个形式非常简单,误差越大梯度越大,模型学得越快。而 MSE 的梯度还要再乘一项 ( p(1-p) ),当 p 接近0或1时梯度会变得非常小,训练速度就会明显变慢。笔试里不要求推导,但你得知道结论,这个结论通常能帮你快速排除错误选项。
优化器那边,考了SGD、 Momentum、RMSProp、Adam 的区别。关键记忆点有两个:第一,只有带动量的优化器才会在更新时考虑“历史梯度的指数衰减平均”,用来抑制震荡;第二,RMSProp 和 Adam 都对梯度做了逐参数的自适应缩放,区别是 Adam 还额外加了一阶动量。如果选项里出现“Adam 相当于 Momentum + RMSProp”,那基本是正确的。
2.3 工程部署相关:FP16、BF16、TF32浮点数选型
今年卷子里出现了一道很有区分度的题目,问的是“在英伟达Ampere架构GPU上进行混合精度训练时,以下关于FP16、BF16、TF32的说法,哪个是错误的”。这个知识点如果只盯着模型结构准备,很容易完全不会。
先理清几个概念。FP32是单精度浮点数,1位符号位、8位指数位、23位尾数位,训练时的默认精度。FP16是半精度,1位符号位、5位指数位、10位尾数位,优点是计算快、省显存,缺点是数值范围窄,容易溢出。BF16也是16位,1位符号位、8位指数位、7位尾数位,指数范围和FP32一样,所以不容易溢出,但尾数精度比FP16还低。TF32是英伟达Ampere架构引入的一种格式,它不是独立的存储格式,而是计算时默认把FP32的输入截断到19位(1位符号位、8位指数位、10位尾数位),用于Tensor Core加速矩阵乘法的精度通常不低于FP16。
那道题的错误选项大概是“TF32的存储占用和FP32相同”。这就错了,TF32虽然保留了FP32的指数位宽度,但它在计算时尾数位被截断成10位,实际上Tensor Core会把输入转换成19位格式来计算,和FP32的23位尾数相比,精度是有损失的。如果题目问的是“为什么混合精度训练中BF16常用于大模型预训练”,答案应该选“BF16动态范围大,不容易溢出,训练更稳定”。
浮点数这类题目,表面上考的是格式,实际考的是你是否理解模型训练过程中的数值稳定性。我建议准备笔试时,不光要背每个格式的位数分配,还要知道它们分别用在什么场景:FP32用于默认训练,FP16和BF16用于混合精度训练,TF32主要用于A100这类GPU上的矩阵乘法加速。知道这层关系,遇到换着花样的选项也不慌。
3. 数据结构与算法题:代码题从暴力到最优解
3.1 KMP next数组:一道送分题变成失分题
有道题是给一个模式串 P = “abacaba”,要求写出它的 next 数组,next[i] 定义为“模式串前 i 个字符组成的子串中,最长相同前后缀的长度”。这类题目其实在《数据结构》里属于必学内容,但很多人刷LeetCode都不太手算KMP,考场上我花了足足三分钟才确定结果。
求解过程是这样的:先看整个字符串 “abacaba”,最长相同前后缀是“aba”,长度3,所以 next[7]=3。再看前6个字符 “abacab”,最长相同前后缀是“ab”,长度2,所以 next[6]=2。前5个字符 “abaca”,最长相同前后缀是“a”,长度1,所以 next[5]=1。前4个字符 “abac”,没有相同前后缀,所以 next[4]=0。前3个字符 “aba”,最长相同前后缀是“a”,长度1,所以 next[3]=1。前2个字符 “ab”,没有相同前后缀,所以 next[2]=0。前1个字符 “a”,没有真前后缀,所以 next[1]=0。
于是完整的 next 数组是 0 0 1 0 1 2 3。当时我旁边有位同学说答案是 0 0 1 0 1 2 3,也有人写成 -1 0 0 1 0 1 2。其实这两个都对应一种KMP实现风格,前者是“最大公共前后缀长度”版本,后者是把所有下标减1的“失败链接”版本。问题在于,如果你平时写代码用的是“next[0]=-1”的写法,遇到选择题却只给“最大前后缀长度”选项,就很容易看错。所以准备笔试时,最好把两种约定都记清楚,并且理解它们是同一个逻辑的不同表述。理解这一点,比背任何一个数组都重要。
3.2 排序算法与复杂度:高频选择题
算法选择题里,排序几乎是必考。今年网易考了堆排序和快速排序的比较,具体题目大意是:有一组基本有序的数据,问用哪种排序算法最快?答案应该是插入排序,因为数据基本有序时,插入排序的时间复杂度可以退化到 O(n)。而不是快速排序,因为快速排序在数组已经有序或逆序时,如果基准值选得不好,会退化到 O(n²)。
另外有题考查了各种排序算法的空间复杂度。常见的结论要记牢:
- 冒泡排序、插入排序、选择排序:平均时间复杂度 O(n²),空间复杂度 O(1)
- 归并排序:平均时间复杂度 O(n log n),空间复杂度 O(n)
- 快速排序:平均时间复杂度 O(n log n),最坏 O(n²),空间复杂度 O(log n) 到 O(n)
- 堆排序:平均和最坏时间复杂度都是 O(n log n),空间复杂度 O(1)
考试时容易混淆的一点是:堆排序和归并排序的最坏时间复杂度都是 O(n log n),但堆排序空间占用小,归并排序需要额外 O(n) 的辅助空间。如果题目问“哪种排序算法不适合大数组排序”,答案多半选归并排序,因为内存占用大。如果题目问“哪种排序算法利用了大根堆的性质”,则选堆排序。
3.3 笔试实战:一道“求第K大数”的完整复盘
编程题里有一道题让我印象很深:给定一个无序整数数组,求第 K 大的元素。这个题LeetCode上有原型,但网易给了两个版本:一个数组长度 n 较小(n ≤ 1000),另一个 n 非常大(n ≤ 10⁷)。实际考场上给出的数据范围决定了用哪种算法。
n 较小时,直接调用nth_element或者排序后取值都可以。n 非常大时,排序显然不现实,最佳方案是维护一个大小为 K 的最小堆,遍历数组时如果当前元素比堆顶大,就弹出堆顶,把当前元素入堆。最后堆顶就是第 K 大的元素。时间复杂度 O(n log K),空间复杂度 O(K)。
我考场上用C++写了一版:
#include <bits/stdc++.h> using namespace std; int findKthLargest(vector<int>& nums, int k) { priority_queue<int, vector<int>, greater<int>> minHeap; for (int num : nums) { if (minHeap.size() < k) { minHeap.push(num); } else if (num > minHeap.top()) { minHeap.pop(); minHeap.push(num); } } return minHeap.top(); }这题的边界条件主要有两个:一是 K 可能等于1,这时堆只有一个元素,每次比较大根堆逻辑要小心;二是 K 可能等于 n,这时相当于求最小值,如果数组长度为1,minHeap.top()直接返回那个唯一元素即可,不会出错。如果 K 比数组长度还大,题目一般不会给这样用例,但保险起见可以先判断。
复盘时我又想了一遍更优解法:如果允许修改数组,可以用快速选择算法,平均时间复杂度 O(n),最坏 O(n²)。笔试时我不太建议用快速选择,因为它的最坏情况退化比较难预估,而且面试官更希望你写出“稳定、可解释”的堆解法。堆解法虽然慢一些,但时间复杂度稳定,面试问起来也容易讲清楚,是笔试环境下的稳妥之选。
4. 常见问题与避坑技巧
4.1 时间分配不当导致编程题没做完
这是我在牛客网上看到不少人反馈的问题,也是我自己差点踩的坑。网易笔试的编程题不是非常难,但前面选择题和填空题比较花时间,尤其是多选题,漏选、错选都不得分。如果你前面耗时太久,编程题即使想到思路,也可能因为没时间调试而提交失败。
我的建议是:先快速浏览一遍整张卷子,把编程题的时间预留出来。如果一道选择题思考超过两分钟,先标记跳过,等做完编程题再回来。编程题哪怕只AC一道,也比选择题硬磨半小时要划算。另外,现在在线笔试系统普遍支持“分题计时”,但整体时间是固定的,所以必须有全局时间感。准考证上一般会写明总时长,建议按“编程题至少60分钟,选择题和填空最多90分钟”来切分。
4.2 网上找不到原题?用知识点反查
提前批笔试题有保密协议,考后一般搜不到原题。但网上会有很多往届生写的“考点回忆”,比如“今年考了FP16和BF16的区别”“考了KMP的next数组计算”“考了几分排序算法稳定性”。这些回忆虽然没有完整题目,但已经足够帮你锁定复习范围。我在提前批准备阶段,就是靠牛客网上近两年的网易笔试讨论帖,把高频考点整理成了一份清单,再按清单逐项补基础。
这种方法比盲目刷题更高效。因为大厂笔试的知识点范围其实是有限的,浮点数格式、KMP、感受野、排序算法、优化器、卷积计算这类“经典考点”会反复出现,只是每年换一个问法。你只要把每个考点背后的原理搞明白,题目怎么变都能应付。把考点整理成自己的“反查清单”以后,看到任何一条新热词,都能快速定位,也不会被带偏。
4.3 环境与输入输出问题:线上笔试的隐形杀手
网易的在线笔试环境一般支持本机IDE调试,再提交到系统判题。这个流程看起来简单,实际上挺多坑。首先是语言版本问题,比如你用C++提交,系统可能默认是C++14,如果你代码里用了C++17的新特性,比如std::optional,直接编译报错,这类问题不是算法问题,却是实实在在的失分原因。建议考前先确认支持的语言标准,写代码时尽量用最基础的语法。
其次是输入输出格式。在线判题系统普遍要求从标准输入读取数据,输出到标准输出,不需要也不允许用文件读写。很多同学平时在本地IDE里写代码习惯读文件,考场上忘了改,就会导致“本地跑得通,提交零分”。我自己的习惯是封装两个函数:一个solve负责读数据并计算,一个main只调用solve。这样切换文件输入和标准输入时,只需要改main里的一行代码,非常方便。
4.4 考前一周应该重点复习什么
如果距离笔试只剩一周,我不建议再开新题了。优先级最高的是错题和易混淆知识点,其次是自己做过的编程题思路总结,最后才是查漏补缺。深度学习基础方面,重点看卷积输出尺寸计算、BN在训练和推理时的不同行为、各种损失函数的适用场景;算法方面,重点看KMP的 next 数组手动推导、快排和堆排的过程、二叉树和链表的常见题型;数值方面,重点看FP16和BF16的位数分配,以及训练时为什么需要 loss scaling。
考前前一天可以把上面这些整理成一份简洁的速查卡,不用背,浏览一遍就行。我吃过亏的地方在于:花了大量时间刷LeetCode困难题,结果笔试反而考了不少“概念性”选择题,比如“感受野公式”“浮点格式”“KMP next 数组”,这些不用写代码,但不会就是不会。所以对深度学习算法岗来说,笔试复习一定要“基础理论 + 代码题”两条腿走路,缺一不可。
5. 一些亲测有效的准备方法和心路历程
5.1 复盘比刷新题更重要
考完提前批笔试后,我没有立刻进入下一家笔试的准备,而是花了一天时间把考试的题目尽量回忆出来,分门别类整理成了自己的错题本。复盘时我发现一个规律:真正让我犹豫的题目,往往不是需要深度的题目,而是“好像知道但又记不准”的题目。比如浮点数那题,我知道FP16、BF16、TF32的名字,但没仔细对比过指数位和尾数位的分配,考场上只能靠排除法猜。这种模糊地带,在深度学习算法工程师的笔试里往往就是拉分项。
所以准备过程中,我会建议你专门给“概念对比”留出一个文档,把所有相似概念放在一起对比着记。比如:FP32/FP16/BF16/TF32、BN/LN/IN、SGD/Momentum/RMSProp/Adam、交叉熵/MSE、卷积/池化/全连接。用表格把位数、公式、适用场景列出来,不看不知道,一看就能发现自己到底哪里没搞懂。这个方法听着笨,但真的比反复刷题更能查漏补缺。
5.2 心理节奏和做题顺序的调整
在线笔试最大的挑战不是题目本身,而是屏幕上那个倒计时。我考完提前批以后回想,有两三道选择题如果放在平时,我肯定能推导出正确答案,但因为时间紧、心理紧张,手一抖就选了错误选项。后来我用牛客网上的模拟卷练习,特意训练自己“先做能拿分、再做难拿分”的顺序:编程题留足时间,选择题不要恋战。这个实战策略很管用,到了考场上心里就稳得多。
还有一个小技巧:当选择题完全没思路时,先排除明显违反常识的选项。比如浮点数那题,选项里出现“FP16的指数位和FP32一样”,这就明显错了,因为FP16如果用8位指数位,那它和BF16的区别就说不通。排除两个错误选项后,即使靠猜,正确率也能从25%提升到50%。
5.3 调整复习权重:笔试和面试的关注点不一样
笔试和面试考察重点其实存在明显差异。面试官更喜欢问项目、问模型结构、问为什么,而笔试更偏爱计算公式、写伪代码、判断概念的准确性。所以如果你已经在准备面试,不要以为笔试可以裸考。我见过项目很扎实的同学,笔试却因为不会手动算 KMP next 数组而挂掉。
建议是在笔试冲刺阶段,把复习重心放到“可计算、可推导”的知识点上来。比如卷积输出尺寸、感受野、参数量和计算量、BN的推理公式、Softmax与交叉熵梯度推导、KMP next 数组、排序算法复杂度、浮点数转换等。这些内容不需要很强的工程能力,但需要你愿意静下心去算、去推。一旦推过一遍,记忆会比单纯背诵牢固得多。
5.4 关注时间线:笔试结束后如何衔接面试
提前批笔试通过后,部门一般会在一到两周内约面试。我个人的经验是,笔试结束不等于可以放松,最好在等结果期间就把简历上的项目细节重新过一遍,尤其是“为什么用这个方案”“有没有对比过其他方案”“指标提升了多少”这类问题。网易的面试很喜欢深挖项目,如果因为你简历上的某个技术名词表述不准确而被追问,然后又答不圆,那比笔试挂掉更可惜。
另外要留意不同岗位的提前批节奏可能不一样。我在牛客网上看到有些同学投的是网易云音乐,有些投的是网易互娱,还有些投的是网易有道,这几个业务的笔试题型都会有差异。互娱方向更偏图形学和游戏AI,云音乐偏推荐系统,有道偏NLP,所以准备时也要结合目标岗位做定向复习。如果时间紧张,优先保证通用基本功,再根据岗位方向补充一到两个专项考点。
6. 最后想对准备2024及以后校招的朋友说几句
网易这套题放到现在来看,依然有很高的参考价值。因为它代表了一类大厂通用型算法岗位的筛选逻辑:先看基础扎不扎实,再看代码能不能落地,最后才看你对深度学习的理解深不深。这个逻辑在未来的校招笔试里大概率还会延续。
建议你从现在开始,把复习资料沉淀成自己的知识库。不只是收藏别人的面经,而是每做完一套题,就把错题和考点整理成属于自己的文档,这样等到投递简历前的冲刺阶段,你只需要复习自己的错题本,而不是从头再刷一遍所有资料。这个方法能节省大量时间,也能精准打击你的知识盲区。
另外,心态上不要把所有希望都寄托在一次提前批上。提前批本身也有很强的“筛人”属性,没进面试不代表你不行,只代表这次准备可能还不够全面。秋招正式批还有机会,很多人在提前批挂了之后,在正式批总结教训反而拿到了offer。保持持续学习的状态,比一次成败重要得多。
最后分享一个我后来一直用的小技巧:把笔试中遇到的每一个新知识点,当天就用自己的话写进知识库,并配一个“为什么”的解释。这个过程相当于在潜意识里做了一次输出,记忆效果远好过读十遍资料。你有这些积累以后,不管是笔试还是面试,面对那些“好像哪里见过”的题目,都能更有底气。