这篇文章记录我自毕业以来,为大模型算法岗位面试准备的一些知识。这篇笔记主要基于个人实际经历,希望可供后来者参考。
这篇文章会搜罗和记录各种类型的题目。实际面试的时候,面试官很可能结合实际的简历提问,因此不是所有知识点都同等重要。文章里面有的题目太偏太难,请结合实际经历和目标岗位参考吧。
本人水平有限,文章内容不一定正确,也难称完整,会持续保持更新。
算法基础知识
请简述机器学习中“偏差-方差权衡”的概念,并说明如何通过调整模型复杂度平衡偏差和方差
- 偏差描述的是预测结果和真实目标之间的误差;
- 方差描述的是模型对训练数据波动的敏感程度;
- 如何通过调整模型复杂度平衡偏差和方差:
- 增加模型复杂度——减低偏差
- 降低模型复杂度——降低方差
- 利用正则化控制复杂度——降低方差
- 增加训练数据——降低方差
- 交叉验证,选择最佳复杂度
请解释什么是协同过滤推荐算法,并比较基于用户的协同过滤和基于物品的协同过滤的优缺点。
- 利用用户-物品交互数据,挖掘用户或物品之间的相似性,利用相似性推荐物品
- 基于用户的协同过滤:
- 优点:可发现新物品,适用于物品远多于用户的场景;
- 缺点:用户兴趣易变卦,相似性计算复杂度较高
- 基于物品的协同过滤:
- 优点:物品相似度相对稳定,可解释性强;
- 缺点:难以发现新物品,对小众物品不友好;
请描述卷积操作的计算过程,并说明CNN在图像识别任务中为什么能取得较好的效果
- 卷积计算过程:卷积核在特征图上滑动,对应位置相乘求和,得到对应位置的输出;
- CNN优势:
- 局部感受野
- 权值共享
- 平移不变性
请简述过拟合的定义、产生原因及常用的解决方法。
- 定义:模型过度拟合训练数据,导致在新数据上泛化能力差。
- 产生原因:模型复杂度高于数据复杂度(如参数过多)、训练数据不足或存在噪声、训练轮次过多。解决方法:
- 增加训练数据(如数据增强);
- 降低模型复杂度(如减少网络层数、决策树剪枝);
- 正则化(L1、L2正则化、Dropout);
- 交叉验证(如K折交叉验证);
- 早停(训练中监控验证集误差,及时停止训练)。
交叉熵、熵和KL散度之间的关系是什么?
设\(P\)代表实际数据的分布,\(Q\)是模型预测的分布,那么,
熵的定义: \[ H (P) = -\sum_x P(x) \log P(x) \] 交叉熵: \[ H_\text{CE}(P, Q) = -\sum_x P(x) \log {Q(x)} \]
KL散度的定义: \[ D_\text{KL}(P, Q) = \sum_x P(x) \log {P(x) \over Q(x)} \]
\[ H_\text{CE}(P, Q) = D_\text{KL}(P, Q) + H(P) \]
什么时候优化KL散度等于优化交叉熵?
如前所述,如果数据分布\(H(P)\)为常数(即不随优化过程改变),那么优化KL散度等价于优化交叉熵。
反过来,如果\(P\)随著优化过程变化,那么两个优化目标就不能等价了。
Precision、Recall和F1-Score之间的关系是什么? \[ \text{Precision}=\frac{\text{TP}}{\text{TP} + \text{FP}} \] Precision关心“查得准不准”,即预测为正样本的,多少是真的正样本。
\[ \text{Recall} = \frac{\text{TP}}{\text{TP} + \text{FN}} \] Recall关心“查得全不全”,即所有的正样本,有多少被找回来了。
F1-score是两者的调和平均。\(F1 = \frac{2}{1 / \text{Precision} + 1 / \text{Recall}}\).
F1-score不是两者的算术平均,因为调和平均更能惩罚极端情况:只要两者有一个很低,平均值就会很低。
手写K-Means
TODO
如果特征维度很大而样本又少,应该怎么办?
TODO
MSE和MAE分别作为损失函数会带来什么区别?
特征存在共线性问题会有什么影响?应当如何应对。
TODO
在训练模型的过程中,我们经常遇到过拟合。有哪些常见的应对过拟合的方法?
- 数据层面:增加数据量、数据增强;
- 模型设计层面:Dropout、DropBlock、weight decay;
- 训练策略:迁移学习、部分参数微调、early stop、辅助任务、不确定性估计;
- 推理策略:test-time augmentation;
某银行希望构建一个信用卡欺诈检测模型,用于实时识别交易中的欺诈行为。已知数据集包含交易金额、交易时间、商户类型、用户历史交易次数等特征,且欺诈样本占比仅为0.5%。请回答以下问题:
- 该问题属于什么类型的机器学习任务?数据的主要挑战是什么?
- 针对数据不平衡问题,提出至少3种处理方法,并说明其原理。
- 请设计一个模型构建流程,包括数据预处理、特征工程、模型选择、评估指标和部署考虑因素。
请使用Python实现一个简单的线性回归模型,要求:
- 手动推导并实现最小二乘法求解参数(不调用sklearn等第三方库);
- 输入为特征矩阵\(X\)(
shape为n_samples × n_features)和标签\(y\)(shape为n_samples × 1),输出为模型参数\(w\)和\(b\); - 给出模型训练和预测的伪代码,并解释关键步骤。
答案:略。建议参考《最小二乘法求解直线拟合问题》
LLM数据工程
数据去重为什么重要?有哪些数据去重的方法?
- 提升训练效率:重复数据让模型浪费算力做无用功,拖慢收敛速度。
- 防止过拟合与记忆:模型会“背下”重复样本,而不是学习泛化规律,导致测试效果差,甚至引发隐私泄露。
- 缓解长尾问题:高频数据重复会加剧数据不平衡,让模型忽视低频但重要的知识。
常用的去重方法:
- 精确去重:直接比对字符串或字节,移除完全相同的样本。常用哈希指纹(如MD5)来加速。
- 近似去重(模糊去重):用于发现内容高度相似但不完全相同的文本。经典算法是MinHash + LSH。对于小规模数据,可以使用编辑距离去重。
偏好对齐方法为什么需要准备成对的训练数据?能不能只给正面数据,不提供负面数据?
我们从以下几点分析这个问题。
1. 数据集中能不能只包含正面数据或负面数据?如果只给正面数据,那么偏好对齐任务便退化为SFT任务。Jiwoo Hong等人(Hong, Lee, and Thorne 2024)指出,随著SFT过程的进行,虽然模型产生训练文本的概率会上升,但被拒绝文本的概率也随之上升。这表明只给出正面数据(只做SFT)是无法作偏好微调的。类似的,可以推测如果只降低负面数据的出现概率,那正面文本的出现概率也会下降。
2. 为什么PPO使用的是成对的正负样本,而不是为每一个回复给出一个准确的分值? - 标注噪声:从标注规范制定的角度看,个例的打分结果会带有噪声,而同时进行成对的数据标注,有助于减少标注噪声。 - 从reward model学习的角度看,reward model学习目标是正确地评估不同回复之间的相对优劣,而不是模型回复的绝对评分,因此标注过程中分数不是必要的。 - 评分的粒度问题:例如,有两个模型回复都打了3分(5分制),那这两个回复间就不能再分出优劣了吗?即使两个回复都打了同样的分数,它们之间的差异也可能提供有价值的信息。 以上讨论说明了为什么PPO为什么采用了成对正负样本进行训练。但是在项目实践中,我也经历过需要标注员标注具体分数的情况。在任务规范清晰、打分标准容易制定的前提下,规定具体的评分标准有好处:
- 详细、严谨的规范有助于减少数据中的噪声;
- 分值间的相对差异大小可以为训练过程提供细化的指导。例如,可参考LLaMa 2(Touvron et al., n.d.)在reward model的损失中加入一个margin component,要求正负样本相似时reward model的打分可以不用差距太大,反之则要求二者的打分要有显著差异。
- 最后,即使是在标注员已经提供了具体分值的情况下,我们仍要求标注员对所有回复进行排序,即使有些模型回复的分值是相同的。不同标注员的排序一致性是评价数据质量的一个重要指标。
3. 训练成本。 从训练成本的角度来看,成对的偏好训练数据更容易大规模收集(例如可以在用户点踩的时候立即给出另一个备选回复),标注成本也更低。
预训练/后训练/偏好对齐/强化学习
LoRA训练的时候,如何选择冻结的层?
LoRA本身只插入低秩适配器(Adapter)矩阵,原始权重是冻结的,因此问题的关键不在于「冻结哪些层」,而在于「对哪些层应用LoRA适配器」。常见的选择策略如下:
- 只对Attention的Q和V矩阵应用LoRA:这是LoRA原始论文的做法,在参数量和效果之间取得平衡。Q和V的调整对模型行为影响较大,足以适应下游任务。
- 对所有Attention层(Q/K/V/O)应用LoRA:当下游任务与预训练分布差异较大时(如代码生成、数学推理),对所有Attention投影矩阵都应用LoRA可以提升适配能力。
- 对FFN层也应用LoRA:FFN层存储了大量知识,在需要注入新知识或改变模型行为模式时(如长CoT训练),对FFN层也应用LoRA有帮助。
- 根据层深度选择:底层(靠近输入)捕获通用语法/句法特征,高层(靠近输出)更偏向语义和任务相关特征。因此:
- 若任务与原始领域差异不大,可只对高层应用LoRA;
- 若任务需要大幅改变模型行为,建议对所有层均应用LoRA。
- 根据参数重要性选择:可计算各层权重的Fisher Information或梯度幅值,选择重要性高的层应用LoRA,减少冗余参数。
实践中,最常见的配置是对所有Attention的Q和V矩阵应用LoRA(rank=8~64),再根据任务复杂度决定是否扩展到其他层。
PPO的critic model是做什么的,为什么需要critic model?
Critic model(也称value model)在PPO中负责估计状态价值函数 \(V(s)\),即给定当前状态(已生成的token序列),预测从该状态出发能获得的期望累积奖励。
为什么需要critic model?
- 降低梯度方差(Variance Reduction):PPO使用advantage函数 \(A(s,a) = Q(s,a) - V(s)\) 来衡量某个动作相对于平均水平的优势。直接使用reward作为信号,梯度方差极大,训练不稳定。减去baseline \(V(s)\) 后,方差显著降低。
- 提供逐位置的学习信号:reward model只在完整序列结束时给出一个奖励(稀疏奖励),而critic model可以在每个token位置估计未来回报,使得actor model在每个生成步骤都能获得学习信号。
- GAE(Generalized Advantage Estimation)的依赖:PPO通常使用GAE来计算advantage,GAE需要在每个时间步都有value预测,以平衡bias和variance的trade-off。
为什么不能直接用reward model代替critic model?
Reward model给出的是完整序列的奖励评分,而critic model给出的是当前状态的期望回报。二者角色不同:reward model是环境提供的评分信号,critic model是对该评分信号的拟合,用于降低方差。如果去掉critic model,PPO就退化为REINFORCE算法,方差会大幅增加,训练难以收敛。
为什么GRPO可以成功去除PPO的critic model?
核心改进在于用group内的相对优势替代critic model的value估计。
具体来说:
Group-based advantage估计:GRPO对同一个prompt采样多个回复(一个group),然后用group内各回复的reward相对于group均值的标准化分数作为advantage: \[A_i = \frac{r_i - \text{mean}(r_1,\dots,r_G)}{\text{std}(r_1,\dots,r_G)}\] 这本质上是用group内的其他样本作为baseline,替代了critic model的value估计。
为什么可以去掉critic model?
- Critic model的作用是提供baseline以降低方差。GRPO利用同一prompt下多个采样结果的reward分布来构造baseline,同样能达到降低方差的效果。
- 省去critic model意味著减少了约一半的训练参数(value model通常与policy model规模相当),显著降低了显存和计算开销。
- 避免了critic model训练不稳定或拟合不准确带来的额外噪声。
GRPO的局限:GRPO依赖于group内样本的多样性。如果group内所有回复的reward相近(方差小),则advantage信号微弱,训练效率降低。这也是后续DAPO提出Dynamic Sampling(丢弃reward方差为0的group)的原因。
GRPO方法在后续其它的工作中得到哪些改进?
DAPO:
- Clip higher:DAPO观察到训练过程中,actor model的entropy在不断降低,导致reward趋同且探索不足,故提出修改PPO-Clip策略,允许clip的下界和上界设置不同的值。通过提高上界,可以鼓励探索。
- 移除KL散度。DAPO认为在长CoT训练的过程中,模型参数显著偏离原模型是寻常的,而且移除KL散度项不会严重影响效果;
- Dynamic Sampling:GRPO需要group内有reward差异,否则当前组内数据就不会产生梯度。如果一组rollout没有reward variance,DAPO会直接将其丢弃。
- Token-Level Loss:原GRPO会进行sequence-level的loss平均,这会导致长回复的sample中,每个token的权重变小。DAPO采取token级别的平均,有利于长思维链学习。
- Overlong Reward Shaping:GRPO通常采用truncation和简单的超长惩罚。DAPO提出length-aware penaly,在超长时给予线性的惩罚(从0线性降低到-1)。
GSPO:
- 序列级重要性采样:GRPO逐token算重要性权重,长序列方差高,噪声累积;GSPO用整个序列的联合概率比(几何平均),所有token等权重;
- 序列级裁剪与奖励:裁剪和奖励分配都改为全序列级别;
详细介绍Clip higher这个技巧。
DAPO采用的目标函数具备这样的形式: \[ \begin{aligned} \mathcal J(\mathbf \theta) &= \mathbb E \left[ \frac{1}{\sum_{i=1}^G|o_i|} \sum_{i=1}^G \sum_{t=1}^{|o_i|} \min(r_{i, t}(\mathbf \theta) \hat A_{i, t}, clip( r_{i, t}(\mathbf \theta), 1 - \epsilon_\text{low}, 1 + \epsilon_\text{high} )\hat A_{i, t} ) \right]\\ \end{aligned} \] 我们分情况讨论\(\min(r_{i, t}(\mathbf \theta) \hat A_{i, t}, clip( r_{i, t}(\mathbf \theta), 1 - \epsilon_\text{low}, 1 + \epsilon_\text{high} )\hat A_{i, t} )\)这个核心算式:
- 如果\(r_{i, t}(\theta)\)处于上下截断界限之间,公式简化为\(r_{i, t}(\mathbf \theta)\hat A_{i, t}\)这样的简单情形,我们不做更多讨论;
- 如果\(r_{i, t}(\theta)\)被截断为\(1 - \epsilon_\text{low}\),代表此时policy model比reference model更排斥使用当前action,而且程度相差过大。公式变成\(\min(r_{i, t}(\mathbf \theta) \hat A_{i, t}, (1 - \epsilon_\text{low})\hat A_{i, t})\),此时如果\(\hat A_{i, t}<0\),\(\theta\)的梯度就被截断,阻止policy model进一步降低当前action的输出概率;
- 如果\(r_{i, t}(\theta)\)被截断为\(1 + \epsilon_\text{high}\),代表此时policy model比reference model更偏好使用当前action,且程度相差过大。公式变成\(\min(r_{i, t}(\mathbf \theta) \hat A_{i, t}, (1 + \epsilon_\text{high})\hat A_{i, t})\),此时如果\(\hat A_{i, t} > 0\),\(\theta\)的梯度就消失,阻止policy model进一步增大对当前action的偏好;
DAPO将\(\epsilon_\text{low}\)和\(\epsilon_\text{high}\)设置为不同的值,并适当提高\(\epsilon_\text{high}\),从而允许模型在\(\hat A_{i, t}\)为正的时候,减少对提高“exploitation token”概率的限制,从而鼓励exploitation。
有时关闭DAPO的dynamic sampling,效果反而更好。为什么?
因为DAPO的重新采样改变了数据分布。例如模型已经高概率答对了某个问题,但dynamic sampling会重新采样,直到出现负例,导致模型过度训练。
为什么要做偏好对齐?只做SFT不行吗?
只做SFT不做偏好对齐经常会导致一些问题。
1. 从训练目标来看:预训练、SFT任务本质上都是要求模型预测下一个token,而这与具体业务场景的目标——例如”以安全和有益的方式遵循人类指令”——是不一致的(Ouyang et al. 2022)。这种训练目标的不对齐导致模型容易产生不安全、不忠实或有害的信息。 2. 曝光偏差问题:在预训练、SFT阶段,模型只是在训练数据的分布上进行下一个token的预测。一些基于强化学习的偏好对齐任务提供了一种在模型自身预测分布上进行训练的方法(Ranzato et al. 2015),缓解了曝光偏差问题。
为什么SFT后的大模型会有幻觉?
模型的幻觉是指模型误认为自己能感知到一些实际感知不到的东西,或者以非常肯定的态度给出错误信息。例如,问”现在是几点钟?“,模型在不接入外部插件的情况下回答”现在是下午三点”,这便属于模型幻觉。
预训练、SFT任务本质上都是要求模型预测下一个token,而这与AI应用的目标——“以安全和有益的方式遵循人类指令”是不一致的(Ouyang et al. 2022)。这种训练目标的不对齐导致模型容易产生不安全、不忠实或有害的信息,例如导致模型幻觉。
什么是对齐税?如何减少对齐税?
在经过偏好对齐后,模型的通用能力可能下降,比如可能出现更多地生成不适当的”拒绝回答”现象,这一问题被称为”对齐税”。 那么,我们要如何减少”对齐税”,让经过RLHF的模型尽量保持RLHF之前的能力呢?
- 训练时不要偏离reference太多:在PPO算法中,除了reward model、policy model,还有一个reference model(同样由SFT后的模型初始化得到)。PPO算法会限制policy model不过分偏离reference model。这可以视为一种限制policy model能力偏移的机制。(尽管reference model的作用不止于此。)InstructGPT(Ouyang et al. 2022)指出通过在PPO更新步骤中混入增加预训练分布的对数似然可以大幅降低模型的能力衰退。
- 强化学习/偏好对齐不一定带来对齐税:尽管多数研究未能消除偏好对齐对模型性能的损害,但也存在证据表明负对齐税是可能实现的。OpenAI对大模型数学答题能力的一项研究(Lightman et al. 2023)显示以合适的reward model设计为前提,模型的数学回答正确率能够提升。尽管这项工作是针对数学任务进行的,但这一结论有可能可以推广到一般的文本生成任务。
SFT过程中会出现灾难性遗忘吗?如何应对灾难性遗忘?
一般我们在项目中会通过SFT来获得一个面向专门领域和特定任务的模型,例如心理诊疗模型、法律咨询模型、医疗建议模型、论文修改模型。SFT的过程中大模型很容易忘记对象任务以外的知识,损失一部分通用能力。就目前的技术来看,灾难性遗忘问题是不可避免的,但可以通过以下方法缓解:
- 限制偏离基模的程度:使用基模作为教师模型,引入KL散度约束,使模型的token分布不要过于偏离预训练模型。
- 数据重放:一种直接的做法是在SFT阶段随机地采样一部分预训练数据。(经验上看,预训练数据的采样比例在SFT阶段会起到比较大的影响。)
- 部分参数微调:与全量微调不同,可考虑采用LoRA、Soft Prompt等方法,通过调控参与训练的参数量来控制SFT的拟合程度。(个人实践中还是全量微调比较多。)
- 其它训练参数的调整:可以尝试调整学习率、epoch数等参数,使用early stop等技巧减少过拟合和遗忘现象。一般训练1到3个epoch、损失降到0.5~0.3以下就好。训练太多,训练集拟合得太厉害也不是好事。
除了在训练阶段尽量缓解灾难性遗忘问题,去弥补训练后已经发生的遗忘也是一种可取思路。以下是一些工程上可以选择的方法:
- 外部知识库/知识图谱/检索增强:实际上指望大模型记住各种各样琐碎的知识是十分困难的。相比之下,赋予大模型随时调用外部知识库是一种弥补知识缺陷的有效方法。
- 一些相关的学术工作:LoRAMoE(Dou et al. 2024)通过冻结主干网络,迫使LoRA利用一部分世界知识来缓解灾难性遗忘。
当前RLHF的流程有什么值得优化的地方?
这是一个开放性问题。言之有理地回答即可。
个人认为可以讨论的点:
- 更细粒度的reward:如Let’s Verify Step by Step(Lightman et al. 2023)一文指出的那样,偏好对齐可有更细粒度的标注,给出更准确的reward反馈。现在普遍常见的偏好数据只针对完整的回答给出标注,而不是针对每一个句子给出标注。相比于标注整个回答的偏好,仅标注出第一个有问题的句子/步骤不会增加更多的标注代价,但能够提升reward信号的精细程度。这样的细粒度偏好数据是目前比较少见的,有待建设;
- BT模型的优化和精细化:大多数偏好对齐方法采用了BT模型。BT模型要求reward model拉开正负样本的得分差距,但并没有考虑到正负样本间差异以及偏好程度差异的强弱问题。直觉上,对于回复相似,偏好差异不明显的情况,应该给与比较低的权重;对于回复差异大,偏好对比明显的情况,应该给与比较高的权重。一些工作(Touvron et al., n.d.; Meng, Xia, and Chen 2024)尝试了人为设置规则或调整超参来调整BT model的margin,但也许还有自适应地调整BT model的margin且取得更好对齐效果的方法存在。
旋转位置编码相对于原本的绝对位置编码有何改进?
TODO
为什么Transformer模型要使用LayerNorm而不是BatchNorm。
TODO
介绍RMSNorm。
观察到LayerNorm的成功在于缩放的稳定性,而中心化操作是可有可无的。故RMSNorm选择只进行缩放,去除了中心化操作,减少了计算开销;
class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.eps = eps
# 可学习的缩放参数 Gamma,初始化为全 1
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x: torch.Tensor) -> torch.Tensor:
# x 的形状通常是 (batch_size, seq_len, hidden_size)
# 1. 计算平方后的平均值,保留最后一个维度 (keepdim=True)
variance = x.pow(2).mean(-1, keepdim=True)
# 2. 计算 1 / RMS(x)
# 使用 torch.rsqrt(t) 相当于 1 / torch.sqrt(t),计算速度更快
rsqrt = torch.rsqrt(variance + self.eps)
# 3. 归一化并乘上可学习参数
return x * rsqrt * self.weight请介绍几种你熟悉的tokenization方法。
TODO
Encoder-Decoder结构、Prefix LM和Causal LM有什么不同?
T5论文的Figure 4很好地展示了三者的不同。 Encoder-Decoder结构下,Encoder的输入token间是两两互相可见的,Decoder的attention mask则是单向可见的。
Prefix LM和Causal LM的结构相近,区别是Prefix LM中,前缀文本内的token是两两互相可见的,前缀文本后的token是单向可见的;而Causal LM中所有token都是单向可见的。
Prefix LM和Encoder-Decoder相比是类似的,都允许一部分token之间两两完全可见,但Prefix LM好比是实现了Encoder和Decoder的参数共享,减少了参数量。
当下Causal LM是被大模型广泛采用的架构。虽然Encoder-Decoder和Prefix LM的双向注意力理论上有优势,但仍然被大模型领域所”淘汰”。这就引出了另一个问题,“为什么大模型都采用Causal LM结构”
为什么大模型都采用Causal LM结构?
- KV-Cache优化:Encoder-Decoder和Prefix LM都不易采用KV-Cache优化,而支持KV-Cache的Causal LM对推理加速更友好;
- 训练效率:Encoder-Decoder架构模型的Pipeline Parallel难以优化。即使模型架构能带来理论上的性能优势,但是却带来昂贵的训练代价的话,那么这种模型结构就会输给那些更能充分利用算力的模型;
- 轨迹依赖:OpenAI使用Causal LM成功实现了GPT系列大模型,这使得后来者都更倾向于采用相近的配置进行实验。
Qwen 3.5有哪些重要的优化点?
- 混合注意力机制:Gated Delta Network + 标准注意力。其中75%的层采用了混合注意力;
- 极致稀疏的MoE架构,极低的激活比。397B激活17B,35B激活3B;
- 原生多模态+early fusion,使用视觉token,而不是文本模型+外挂视觉;
- 原生多token预测支持
讲讲MOE的原理,为什么MOE的参数量大但计算量不大?
核心思想是将FFN层替换为N个专家网络+门控路由器,每个token只路由到Top-k个专家(通常K=1或者2),这就实现了控制计算量,但增大参数量。
路由的计算: \[ g(x) = \text{softmax}(W_g \cdot x) \] 根据上式的结果选择Top-K个专家,加权混合。
(DeepSeek-V3引入共享专家,共享专家一定会被激活,负责通用知识,路由专家由Top-K决定是否激活,负责领域知识。)
MOE的负载均衡怎么做?
为了避免训练时路由器总是把token分给少数专家,引入Aux Loss: \[ L_\text{aux} = \alpha \cdot N \cdot \sum_{i=1}^N f_i \cdot p_i \] 其中\(f_i\)是专家\(i\)被选中的token比例,\(p_i\)是专家的门控概率。
专家被均匀分配时,上式取得最小值,最小值为\(1\)。
为什么不是\(p_i * p_i\),而是\(f_i * p_i\)呢? 因为MOE使用Top-K选择专家,因此存在\(p_i\)比较小(只是略大于没有选中的专家),但是\(f_i\)很大的情况。因此需要结合实际频率\(f_i\)和预测的权重\(p_i\).
至于\(f_i * f_i\)肯定是不行的,因为\(f_i\)不可导。
为什么MOE使用Top-K而不是随机采样呢?
- 随机采样可能会导致专家“同质化”,因为每个专家都有概率在不应该被路由的时候被路由,不得不学习一些通用知识;
- 随机性会给并行化的训练工程带来一些麻烦。
MOE路由是batch维度的还是token维度的?
- Batch维度:同一batch内所有token走同一个专家,引入了batch内的互相干扰,不合理。
- Token维度:合理,但是实现复杂,需要All-to-All通信。例如,先将
(batch_size, seq_len, hidden_dim)的矩阵展平为(batch_size * seq_len, hidden_dim),即不考虑batch的概念,然后分别计算每个token的去向,将输入送到对应的计算设备上。
OPD (On-Policy Distillation)中,KL散度如何设计?
正向KL: \[ D_\text{KL}(P_\text{base}\Vert P_\theta) = \sum_x P_\text{base}(x)\log \frac{P_\text{base}(x)}{P_\theta(x)} \]
- 特性:如果教师在某个token上的概率很高,那么学生必须在这个token上分配概率,否则KL散度会爆炸,导致惩罚极大
- 导向:学生会试图“包住”教师的行为
- 缺点:小模型很难“包住”或者模仿大模型,导致训练质量差。
逆向KL: \[ D_\text{KL} (P_\theta \Vert P_\text{base}) = \sum_x P_\theta(x) \log\frac{ P_\theta(x) }{ P_\text{base}(x) } \] 这是OPD最常采用的预设。
- 特性:如果教师分布在某处为0,那么学生在这个点的概率也需要趋近。相反,如果教师有若干个候选行为,学生只需要学会其中的一个模式;
- 导向:学生模型会表现得保守但精准,只说自己有把握,教师认可的话;
- 缺点:多样性崩塌,同质化,Pass@k下滑;
目前前沿方法会采用自适应散度,在高熵、高不确定的情况下切到正向KL,鼓励探索;在教师确定性强的情况下使用逆向KL收敛精度。
SFT和RL对基座大模型的作用和影响有什么区别?
- SFT:
- 作用:通过提供特定格式、特定任务的数据,使其适应下游任务
- 影响:SFT倾向于让模型记忆训练数据,模型难以泛化到分布外的数据;但是SFT的训练有助于稳定模型的输出格式,使得后续的RL可以稳定进行
- RL:
- 作用:对齐特定的人类偏好,或者通过最大化设计的reward来解决某些类型的任务;
- 影响:展现出较强的泛化能力;在多模态任务中也能提升模型潜在的视觉识别能力;
类 Transformer 模型计算量(FLOPs)精准推导
在评估 Transformer 模型的计算复杂度时,通常以 FLOPs(Floating Point Operations,浮点运算次数) 作为衡量指标。将矩阵乘法中的一次「乘加操作(Multiply-Accumulate)」计为 2 个 FLOPs(1 次乘法 + 1 次加法)。
以下为单个 Transformer Block(层)在一次前向传播(Forward Pass)中,处理长度为 \(n\) 的序列时的计算量推导。推导使用如下符号:
- \(n\):序列长度(Sequence Length / Token 数)
- \(d\):模型隐藏层维度(Hidden Size / \(d_{model}\))
- \(m\):FFN(前馈网路)的中间层维度(Intermediate Size)
- \(L\):模型的总层数(Layers)
一个标准的 Transformer 层主要由两部分组成:自注意力机制(Self-Attention) 与 前馈网路(FFN)。
自注意力的计算可以拆解为以下四个主要矩阵运算步骤:
- Q, K, V 投影矩阵(Projection)
- 将输入的张量 \([n, d]\) 分别乘以 \(W_Q, W_K, W_V\)(三个权重矩阵大小皆为 \([d, d]\))。
- 计算量:\(3 \times (2 \times n \times d \times d) = 6nd^2\)
- 注意力分数计算(\(QK^T\))
- 矩阵乘法:\([n, d] \times [d, n] \rightarrow [n, n]\)。
- 计算量:\(2 \times n \times d \times n = 2n^2d\)
- 权重与 V 相乘(Attention Value)
- 矩阵乘法:\([n, n] \times [n, d] \rightarrow [n, d]\)(此处忽略 Softmax 的少量计算)。
- 计算量:\(2 \times n \times n \times d = 2n^2d\)
- 输出投影矩阵(Output Projection)
- 将结果乘以 \(W_O\) 矩阵(大小为 \([d, d]\))。
- 计算量:\(2 \times n \times d \times d = 2nd^2\)
Attention 小计:\((6nd^2 + 2nd^2) + (2n^2d + 2n^2d) = 8nd^2 + 4n^2d\)
根据架构不同,FFN 的计算量有所差异:
采用两层线性映射,中间夹一个激活函数(如 ReLU 或 GELU): 1. 第一层升维:\([n, d] \times [d, m] \rightarrow [n, m]\),计算量为 \(2ndm\) 2. 第二层降维:\([n, m] \times [m, d] \rightarrow [n, d]\),计算量为 \(2ndm\)
传统 FFN 小计:\(4ndm\)
现代 LLM(如 LLaMA、Mistral)采用 SwiGLU 激活函数,需要三个权重矩阵(Gate、Up、Down): 1. Gate 与 Up 矩阵(升维):\(2 \times ([n, d] \times [d, m]) \rightarrow 2 \times 2ndm = 4ndm\) 2. Down 矩阵(降维):\([n, m] \times [m, d] \rightarrow [n, d]\),计算量为 \(2ndm\)
现代 FFN 小计:\(6ndm\)
将上述各部分加总,并乘上总层数 \(L\):
标准 Transformer 总计算量 \[ \text{Total FLOPs (Standard)} = L \times (8nd^2 + 4n^2d + 4ndm) \]
现代 SwiGLU Transformer 总计算量 \[ \text{Total FLOPs (SwiGLU)} = L \times (8nd^2 + 4n^2d + 6ndm) \]
在现代大语言模型(LLM)中,通常隐藏层维度远大于序列长度(\(d \gg n\)),此时 \(4n^2d\) 的占比极小,可近似忽略。
若以传统 FFN 常见的 \(m = 4d\) 代入,前向传播每层的计算量约为: \[ 8nd^2 + 4nd(4d) = 24nd^2 \]
这与模型参数量(密集参数主要集中在这些矩阵中)形成了固定的比例关系。因此业界常使用以下简化公式来快速估算单个 Token 的计算量:
- 前向传播(Forward Pass):\(\approx 2N\) FLOPs / Token
- 反向传播(Backward Pass):\(\approx 4N\) FLOPs / Token(计算梯度与权重更新,工作量为前向的 2 倍)
- 完整训练(Training):\(\approx 6N\) FLOPs / Token
训练总计算量估算公式: \[ \text{Training FLOPs} \approx 6 \times \text{模型参数量}(N) \times \text{训练总 Token 数} \]
Infra
介绍几种包括Megatron等框架的大模型的并行训练的方式。
下表归纳罗列了几种主要的并行方法的特性:
| 并行方式 | 切分对象 | 主要目标 | 是否改变单卡参数量 | 是否改变单卡Activation | 主要通信 | 通信量趋势 | 依赖关系 |
|---|---|---|---|---|---|---|---|
| Data Parallel | Batch | 提高吞吐 | ❌ | ❌ | AllReduce Gradient | O(P) | 无 |
| Distributed Data Parallel | Batch | DP的优化版本 | ❌ | ❌ | AllReduce Gradient | O(P) | 无 |
| Fully Sharded Data Parallel | Batch + Parameters | 降参数显存 | ✅ | ❌ | AllGather + ReduceScatter | O(P) | 无 |
| ZeRO-1 | Optimizer State | 降优化器显存 | ❌ | ❌ | Gradient Sync | O(P) | DP |
| ZeRO-2 | Optimizer + Gradient | 降训练显存 | ❌ | ❌ | ReduceScatter | O(P) | DP |
| ZeRO-3 | 全参数 | 最大化省显存 | ✅ | ❌ | AllGather参数 | O(P) | DP |
| Tensor Parallel | Hidden维 | 扩大模型规模 | ✅ | 部分减少 | AllReduce / AllGather | O(B·S·H) | 无 |
| Sequence Parallel (Megatron) | Activation(seq维) | 降Activation显存 | ❌ | ✅ | Gather/Scatter Activation | O(B·S·H) | TP |
| Expert Parallel | Experts | 扩大MoE模型 | ✅ | ❌ | AllToAll Token Routing | O(Token) | MoE |
| Pipeline Parallel | Layer | 超大模型训练 | ✅ | 部分减少 | Send/Recv Activation | O(B·S·H) | 无 |
| Virtual Pipeline | Layer Chunk | 降流水线气泡 | 同PP | 同PP | Send/Recv | 同PP | PP |
| Context Parallel | Sequence | 长上下文训练 | ❌ | ✅ | KV交换 | O(S·H) | 通常配TP |
| Ulysses SP(Ulysses) | Sequence | 长上下文训练 | ❌ | ✅ | AllToAll | O(S·H) | 无 |
| Ring Attention | Sequence | 超长上下文 | ❌ | ✅ | Ring KV Transfer | O(S·H) | 无 |
简单介绍Context Parallel。
Context Parallel作用于模型的Attention步骤,对query进行切分,但是每个rank都需要看到完整的key和value的副本。
key和value可以在计算时,在rank间交换传输,使每个query可以看到完整的key和value。
简单介绍Ulysses并行
Ulysses来自Deepspeed生态。Ulysses 的核心思路是:在不同的训练阶段,灵活切换数据的切分维度。
进入 Attention 前:将文本的序列长度(Sequence Dimension)均匀切分到各个 GPU 上。
计算 Attention 时:通过通信,把数据转换为按注意力头(Head Dimension)切分,让每个 GPU 拥有“局部完整的序列,但只负责一部分 Head”。
简单介绍Ring Attention
Ring Attention 把传统 Context Parallel 中“一次性 All-Gather 收集齐全部 KV”的方式,改成了在环状通信拓扑中边传输、边计算的流水线模式。Ring Attention是Context Parallel的一种实现。
Agent设计
Agent架构
现在主流有哪几种agent/multi agent架构?
- 单Agent架构
- ReAct架构(最经典)= Reason + Act。核心思想是:
Thought -> Action -> Observation -> Thought,是所有Agent的基础。 - 特点:简单、泛化性强、易扩展、适合聊天Agent - 缺点:长任务、长对话容易漂移、上下文越来越长、规划能力有限 - Plan-and-Execute:把规划和执行拆开。结构是
Planner Agent -> Task List -> Executor Agent. - 特点:长任务更可靠,可控性增强 - 缺点:计划容易过时,动态适应能力一般 - Workflow Agent:本质不是Agent,而是LLM充当结点的工作流。可以分为几种:
- DAG Workflow
- State Machine Agent
- Multi Agent:
- Manager-Worker。Manager负责拆任务,Worker负责执行不同的子任务;
- Debate式:允许Agent之间扮演不同的角色,互相辩论。能够提升推理质量,修正幻觉
- Society:模仿社会/群体行为。理论上扩展性强,但容易失控。
- Blackboard:多个Agent共享一个“黑板”,任何Agent可以读取和写入,接力完成任务。
当前有哪几种管理Agent记忆的方法?
- Working Memory,即当前上下文窗口。几种管理方法:
- Sliding Window:保留最近N轮
- Summarization:总结和压缩历史上下文
- Hierarchical Context:分为Hot Context、Warm Context、Cold Context,类似CPU Cache:
- Hot Context包括:当前对话,最新任务,核心关注的任务。直接在prompt中保留
- Warn Context:中间推理结果、旧代码……等不太重要,但可能有用的东西。采用summary + retrieval的方式保留和检索
- Cold Context:冷上下文,可负责长期记忆。可通过Vector DB、Knowledge Graph等方式实现
- Episodic Memory:情节记忆,不只记知识,而是记经验,例如某次失败是因为token错误,或者用户喜欢简洁回答等。可以通过embedding向量库召回。
- Semantic Memory:语义记忆,记忆知识库,而不是记忆经历、经验。即RAG。
- Procedural Memory:程序性记忆,即执行某些事情,需要经过哪些步骤。skill文档就是一种程序性记忆。
RAG
讲一讲NDCG(Normalized Discounted Cumulative Gain)这个reward/metric。
在介绍NDCG之前,先需要了解DCG和IDCG。
DCG:设召回模型返回n条结果, \[ DCG = \sum_{i=1}^n \frac{ 2^{\text{rel}_i} - 1 }{ \log_2(i+1) } \]
- \(\text{rel}_i\)表示第\(i\)个位置的搜索结果的相关性;我们可以用标注数据来定义相关性,如果第\(i\)个结果在标注的ground truth中,就让它的相关性为1,否则为0.
- 分子:相关性越高,贡献越大
- 分母:位置越靠后,折扣越大,按\(\log\)衰减。这是因为我们假设用户更关心排序靠前的结果。
IDCG:把所有结果按最佳排序(相关性从高到低)时的 DCG。
NDCG:\(\text{NDCG} = {\text{DCG}\over \text{IDCG}}\). 可以看出\(0 \le \text{NDCG} \le 1\). \(0\le \text{NDCG}\)是显然的。而\(\text{NDCG} \le 1\)可以这样理解:
假设存在\(i < j, g_i > g_j\),其中\(g_i, g_j\)是DCG的分子。那么交换这两项带来DCG的差值为: \[ \begin{aligned} \Delta &= {g_i \over \log_2(j+1)} + {g_j \over {\log_2(i+1)}} -({{{g_i} \over {\log_2(i+1)}} +{ g_j \over {\log_2(j+1)}}})\\ &= {g_i - g_j\over\log_2(j+1) } + {g_j - g_i\over \log_2(i+1)}\\ &= (g_j - g_i)(\frac{1}{\log_2(i+1)} - \frac{1}{\log_2(j+1)})\\ & > 0 \end{aligned} \]
因此DCG这类问题的最佳排序方案就是为高权重的位置分配高价值的结果,因此\(\text{NDCG} \le 1\).
CoT
你是如何构造CoT的?
这个要结合实际项目作答。可供参考的简单回答:
- 数据层面:
- 合成数据:利用商用模型搜集合成数据,生成符合特定规则的求解步骤;
- 规则与模板引导:对于垂直领域任务,可以在prompt中设计对应规则模板;
- 数据清洗与过滤:利用verifier或规则检查,滤除错误数据;
- 训练层面:
- SFT:基于prompt、CoT、answer三元组构造训练数据;
- RL方法:可引入过程奖励提升CoT的准确率;利用可验证的任务设计和奖励设计,获得可靠的CoT;
- 推理层面:
- 采样多条CoT,利用Self-Consistency方法选出最优解,或者利用Tree of Thoughts方法进行回溯搜索。
多模态大模型
你能列举几个常见的从视觉编码器的图像特征映射到大模型文本特征的方法吗?
各种多模态融合模块的优缺点? (注意)Q-Former容易破坏visual token之间的空间关系。
在 BLIP-2 或者 Qwen-VL 中,Learnable Query 的作用是什么,为什么在后续版本中,比如 BLIP-3 和 Qwen2-VL 中都不使用了
Learnable Query(可学习查询)在BLIP-2和Qwen-VL中扮演了一个高效的”信息筛选官”角色,将海量的图像特征压缩成大语言模型更容易处理的少量语义标记。而后续模型(如BLIP-3和Qwen2-VL)之所以不再使用它,主要是因为基础模型的能力大幅提升,能够处理更长的上下文,更多的token,使得更简单、更直接的设计成为了新主流。
如果让你从头开始训练一个多模态大模型,你会如何设计模型结构、训练流程和项目流程?
对于多模态大模型,提高图像的分辨率是十分重要的。为什么现在的大模型往往使用如256x256这类低分辨率的图像,无法做到高分辨率呢?
你观察到GPT4-V有哪些问题?为什么会存在这些问题?
计算机视觉相关问题
为什么要对图像做预处理?
- 预处理将数据变换到统一的格式,方便模型学习;
- 通过提前提取有用的特征,裁剪出感兴趣区域来加快计算;
ViT模型中是如何对位置信息进行编码的?
TODO
请比较Transformer和CNN
TODO
Coding
接下来简单记录一些面试过程中实际遇到的或其它面试经验推荐的题。
在面试过程中要求现场手写的算法题常常不会很难,但作答过程中要思路清晰,能够有效地与面试官沟通,尽量减少试错次数,一次通过。
答题之前记得问清楚数据规模,时间复杂度和空间复杂度需求。
- ⭐最长回文子串
- ⭐二叉树的最近公共祖先
- ⭐⭐两数之和变体:设A、B是两个有序数组,找出所有\(i, j\)满足
A[i] + B[j] = k,要求常数空间复杂度,线性时间复杂度。
此题需要注意数组不是严格单调的,即可能连续出现若干个相同的数。编写双指针法时需要注意边界条件,避免漏掉一些解。 - ⭐⭐三数之和:由两数之和扩展而来的一道题。
- ⭐滑动窗口求最大值:要求\(O(n)\)时间复杂度。
- ⭐top k算法:写出\(O(n\log k)\)的基于堆的方法并不困难,但最好能进一步掌握基于快排的\(O(n)\)时间复杂度的解法。
- 🌙岛屿数量:实际上属于求连通域数量的算法,可用DFS/BFS/并查集方法解。
- 🌙反转每对括号间的子串:考察栈的用法。也可以不用栈来解。容易想到\(O(n^2)\)复杂度的算法,但实际也存在\(O(n)\)的解法。
- 🌙搜索选择排序数组:考察二分法。需要做题者注意到选择排序数组的特性。
- 🌙寻找两个正序数组的中位数(困难):更一般的,可以推广为寻找两个正序数组的第k小的数。考察二分法。需要思考应该对哪个量做二分查找。
- 🌙NER的BIO形式标注转start,end形式(例子 38):以命名实体识别(NER)任务为背景的简单算法题。
- 🌙马在棋盘上的概率:可以用动态规划的方法求解,时间复杂度为\(O(n^2k)\),这里\(n\)为棋盘大小,\(k\)为步数。还可以想到本题所执行的动态规划步骤实际上是一种卷积,且每一步的卷积核都是相同的,因此本题可以用快速幂的方式进行优化。
- 🌙整数拆分:将整数\(n\geq 2\)拆分为任意个正整数的和,使其乘积最大。容易想到基于动态规划的解法,但不容易给出\(O(1)\)时间复杂度的,基于数学分析的解法。
- 问题延伸:如果题目改为将整数\(n\geq 2\)拆分为任意个正数的和,使其乘积最大。该如何分析?参考例子 39
- ⭐给定一个字符串和一个词典,问字符串能否由词典中的单词拼接而成。用DFS可解。可考虑引入缓存等策略进行优化。
开放性问题
这一节记录一些自己被问到的开放性问题。问题具体的答案还是需要结合自身经历才行。
在个人技术成长方面,你有什么规划?
一般可以列出自己最近感兴趣的论文和想做的方向,与团队业务相结合就好。
如何平衡团队的业务目标与个人的技术成长?
面试官问出这个问题的时候,基本意味著这个团队是业务导向的,技术上不太强。
ε=(´ο`*)))
回答“优先完成团队目标,业余也会保持对技术的关注”即可。
为什么想要离职/看新机会?
- 仅仅是解释前东家的缺点是不够的。每个公司有每个公司的缺点,不能保证下一家没有相同的问题。一般可以将问题解释为当前公司平台无法提供更好的发展,而现在在看的机会能补充这方面的不足。最好不要表现出自己是当前工作上遇到困难而离职的,而应尽量表现出自己的事业当前处于上升期,但由于某些原因(工作地点、待遇、发展方向、职业规划)而不得不离开。这时可以顺带说说自己有多么适合这个新岗位。重要的是表现出自己与新公司是一个互相成就的关系。
- 说清楚想去哪里,比说为什么离开更重要。
请介绍一下过往最困难的一个项目。
你当前采用的项目方案的亮点是什么?你对项目最重要的贡献是?
是什么使你在项目中具有不可替代性?或者是什么使你具备比他人更高的价值?
拿到offer后的标准问题
拿到Offer后,记得问以下重点问题。可以不直接问,但也需要间接地通过各种渠道去了解。要了解的包括但不限于:
- 绩效的考核方式,KPI的考核情况
- 有没有末位淘汰
- 有没有轮流背D绩效
- 绩效工资占縂工资比例
- 绩效有没有强制分布
- 绩效如何影响年终奖
- 股票和期权的行权规则
- 竞业协议
- 是否需要背竞业协议
- 竞业协议是否与期权等权利挂钩
- 工作强度:可以开门见山地询问新工作的工作强度如何
- 总包、总包构成和职级
- 总包构成:
- 总回报预计多少
- 其中多少是现金,多少由股票/期权构成?
- 公积金缴纳比例
- 职级:公司的职级是如何划分的,如何与互联网的“标准职级”对应?
- 总包构成:
一些经验和感想:
- (不是绝对的)猎头告诉你某公司不加班、965……基本不太可信,还是需要额外信息佐证。
- 竞业协议是不公平的。如果你签了竞业协议,公司就有权阻止你入职同行业内的任何公司,并在你离职后提供一些微小的补偿。但相反的,假如你主动离职去读博、考公,主动希望公司启动竞业协议,公司却可以选择不启动竞业。主动权总是在公司手上。 某些公司,例如拼多多,有很大的概率启动竞业协议,并且在员工离职后派人调查其去向;所以这个问题一定要在入职之前了解清楚。
总结
笔记的重要性:只学习不总结就容易忘。不做笔记不行啊。最好每次面试过后,立即将记录下来,时不时重新过一遍。
平时多练习,多刷题,保持手感。有的公司(例如Minimax)的算法测试会期望给出最优解,面试时 即使把题目做出来了,也可以问一下需不需要继续改进,提一提能想到的改进点。
附录
笔试题
单选题
本节收录一些笔试题。
以下哪种算法不属于监督学习?
A. 线性回归 B. 决策树 C. K-Means D. 支持向量机
答:C
关于过拟合问题,下列说法错误的是?
A. 过拟合是模型在训练集上表现良好,但在测试集上表现较差 B. 增加训练数据量可以缓解过拟合 C. L1正则化通过增加权重的平方和惩罚模型复杂度 D. 交叉验证可以帮助检测过拟合
答:C
在逻辑回归中,模型的输出值表示的是?
A. 连续变量的预测结果 B. 类别概率 C. 特征重要性 D. 损失函数值
答:B
关于决策树的构建过程,下列描述正确的是?
A. 决策树的划分依据仅包括信息增益 B. 决策树倾向于过拟合,需通过剪枝优化 C. 决策树只能处理数值型特征 D. 决策树的深度越深,模型泛化能力越强
答:B
下列哪种方法不属于降维技术
A. PCA B. t-SNE C. 随机森林 D. LDA
答:C
在深度学习中,Batch Normalization的主要作用是?
A. 加速模型训练收敛 B. 减少模型参数数量 C. 防止过拟合 D. 增强特征非线性表达
答:A
关于卷积神经网络(CNN),下列说法错误的是?
A. 卷积层通过局部感受野提取特征 B. 池化层可以降低特征图维度 C. 全连接层通常位于网络的最后 D. CNN无法处理文本数据
答:D
以下哪种评估指标适用于不平衡分类问题?
A. 准确率(Accuracy) B. F1分数 C. 均方误差(MSE) D. R²分数
答:B
在推荐系统中,基于内容的推荐方法的核心思想是?
A. 利用用户之间的相似度推荐物品 B. 利用物品之间的相似度推荐物品 C. 基于用户历史行为预测偏好 D. 基于物品特征与用户偏好的匹配程度
答:D
多选题
下列属于生成式模型的有?
A. 朴素贝叶斯 B. 隐马尔可夫模型(HMM) C. 高斯混合模型(GMM) D. 逻辑回归
答:A、B、C
关于梯度下降算法,下列说法正确的有?
A. 随机梯度下降(SGD)每次使用单个样本更新参数 B. 批量梯度下降(BGD)每次使用全部样本更新参数 C. 动量法(Momentum)可以加速梯度下降的收敛 D. Adam算法结合了动量法和自适应学习率的优点
答:ABCD
下列关于特征工程的描述正确的有?
A. 特征归一化可以消除量纲对模型的影响 B. 独热编码(One-Hot Encoding)适用于处理类别型特征 C. 特征选择的目的是减少特征数量,提高模型效率 D. 特征交叉可以捕捉特征之间的非线性关系
答:A、B、C、D
关于循环神经网络(RNN),下列说法正确的有?
A. RNN可以处理序列数据 B. LSTM通过门控机制缓解梯度消失问题 C. GRU是LSTM的简化版本 D. RNN的输入和输出长度必须相同
答:A、B、C
智力题
本节记录我收集到的一些代码题或者智力题。
两个硬币的后验概率
有两个硬币,一个硬币的两面都是正面;另一个是正常硬币:一面是正面,另一面是反面;
随机选取其中一枚,抛掷五次,都是正面。问这枚硬币是正常硬币的概率是?
解:设\(A=\text{抛掷五次硬币都是正面}\),\(B={这是一枚正常硬币}\)
题目要求\(P(B|A)\)
\[ \begin{aligned} P(B|A) &= P(A|B) P(B) / P(A)\\ &= 0.5^5 \times 0.5 / P(A) \end{aligned} \]
其中
\[ \begin{aligned} P(A) &= P(A|B)P(B) + P(A|\neg B)P(\neg B) \\ &= 0.5^5 \times 0.5 + 1 \times 0.5 \\ \end{aligned} \]
所以
\[ \begin{aligned} P(B|A) &= 0.5^5 \times 0.5/ P(A) \\ &= \frac{0.5^5 \times 0.5} {0.5^5\times 0.5 + 1\times 0.5}\\ &= \frac{1}{1 + 32}\\ &= \frac{1}{33} \end{aligned} \]
NER标注格式转换
描述: 在命名实体识别(NER)任务中,BIO标注方式是常用的标注格式之一。但在某些应用场景下,我们可能需要将BIO格式转换为start,end形式的标注。
请你实现一个Python函数,将BIO格式的NER标注转换为start,end形式。
函数输入:
- tokens: 一个字符串列表,表示分词后的句子
- bio_tags: 一个字符串列表,表示对应的BIO标注
函数输出: 一个字典列表,每个字典包含以下键:
- ‘entity’: 实体文本
- ‘start’: 实体开始位置
- ‘end’: 实体结束位置
- ‘type’: 实体类型
示例:
输入:
tokens = ['John', 'lives', 'in', 'New', 'York', '.']
bio_tags = ['B-PER', 'O', 'O', 'B-LOC', 'I-LOC', 'O']输出:
[
{'entity': 'John', 'start': 0, 'end': 1, 'type': 'PER'},
{'entity': 'New York', 'start': 3, 'end': 5, 'type': 'LOC'}
]请实现函数bio_to_span(tokens, bio_tags)来完成这个转换任务。
实体的end索引应该是实体最后一个词的索引+1
对于单个词的实体,start和end的差值应该是1
需要正确使用连续的多个词组成的实体
def bio_to_span(tokens, bio_tags):
tokens.append('<eos>')
bio_tags.append('O')
start = 0
end = 0
entity = []
typ = None
ret = []
for i, (t, b) in enumerate(zip(tokens, bio_tags)):
if b.startswith('B-'):
start = i
entity = [t]
typ = b[2:]
elif b.startswith('I-'):
assert typ == b[2:], '{} != {}'.format(typ, b[2:])
elif b == 'O':
if len(entity):
end = i
ret.append({
'entity': ''.join(tokens[start:end]),
'start': start,
'end': end,
'type': typ
})
entity.clear()
else:
raise Exception("Invalid BIO label: {}".format(b))
return ret 接下来用一些测试用例检查给出的算法的正确性。在请求面试官检查之前,先自己编写一些测试用例是一个好习惯。
查看测试用例
test_cases = [
# case 1
{
"input": (["我", "爱", "北京", "天安门"], ["O", "O", "B-LOC", "I-LOC"]),
"output": [{'entity': '北京天安门', 'type': 'LOC', 'start': 2, 'end': 4}]
},
# case 2
{
"input": (["他", "是", "一位", "来自", "中国", "的", "医生", ",", "名叫", "张伟"],
["O", "O", "O", "O", "B-LOC", "O", "B-PROF", "O", "O", "B-PER"]),
"output": [{'entity': '中国', 'type': 'LOC', 'start': 4, 'end': 5},
{'entity': '医生', 'type': 'PROF', 'start': 6, 'end': 7},
{'entity': '张伟', 'type': 'PER', 'start': 9, 'end': 10}]
},
# case 3 (空实体)
{
"input": (["今天", "天气", "真好"], ["O", "O", "O"]),
"output": []
},
# case 4
{
'input': (
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
['O', 'B-PER', 'I-PER', 'O', 'B-ORG', 'I-ORG', 'I-ORG', 'O', 'B-LOC', 'O']
),
'output': [{'entity': 'bc', 'type': 'PER', 'start': 1, 'end': 3},
{'entity': 'efg', 'type': 'ORG', 'start': 4, 'end': 7},
{'entity': 'i', 'type': 'LOC', 'start': 8, 'end': 9}]
},
# case 5 (空序列)
{
'input': (
[], []
),
'output': []
},
# case 6 (只有O标签)
{
'input': (['a', 'b', 'c'], ['O', 'O', 'O']),
'output': []
},
# case 7
{
'input': (
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
['B-PER', 'I-PER', 'O', 'B-PER', 'O', 'B-PER', 'I-PER', 'I-PER']
),
'output': [{'entity': 'ab', 'type': 'PER', 'start': 0, 'end': 2},
{'entity': 'd', 'type': 'PER', 'start': 3, 'end': 4},
{'entity': 'fgh', 'type': 'PER', 'start': 5, 'end': 8}]
}
]
for test_case in test_cases:
out = bio_to_span(*test_case['input'])
assert len(out) == len(test_case['output']), 'Length inqueal! {} != {}'.format(out, test_case['output'])
for o1, o2 in zip(out, test_case['output']):
for k in o2.keys():
assert o1[k] == o2[k], '{} != {}'.format(o1, o2)
print('所有测试用例均通过。')例子 39 🌙如何将整数\(n\geq 2\)拆分为任意个正数的和,使其乘积最大?
设\(n\)被拆分为\(k\)个数,容易证明每个数都为\(n/k\)时乘积最大。(先证明\(k=2\)时,\(n\)应该被拆为\(\frac{n}{2} + \frac{n}{2}\);然后通过构造条件满足数学归纳法来证明。)
加下来,设将\(n\)拆为\(k\)份,可知其乘积应为\(f(n, k) = \left(\frac{n}{k}\right)^k\). 先不考虑\(k\)应为正整数的问题,令\(k \in \mathbb R^+\). \(f(n, k)\)是一个连续函数。
\[ \begin{aligned} f(n, k) &= e^{\ln \left(\frac{n}{k}\right)^k} \\ &= e^{k\ln n - k \ln k } \end{aligned} \]
\[ \begin{aligned} \frac{\partial f}{\partial k} &= e^{k\ln n - k \ln k }\cdot (\ln n - \ln k - 1) \end{aligned} \]
\[ \begin{aligned} \frac{\partial f}{\partial k} = 0 & \Rightarrow \ln n - \ln k - 1 = 0\\ & \Rightarrow k = \frac{n}{e} \end{aligned} \]
容易看出\(\frac{\partial f}{\partial k}\)随著\(k\)的增加而单调上升。因此\(k=\frac{n}{e}\)是函数的极大值点。但是\(k\)需要是整数,因此我们可以考虑取\(k=\lceil \frac{n}{e} \rceil\)或\(k=\lfloor \frac{n}{e}\rfloor\),取其中使乘积最大的\(k\)即可。
例子 40 求斐波那契数列1、1、2、3、5、8……的通项公式。
当\(n\geq 3\)时,有\(F_{n} = F_{n-1} + F_{n-2}\). 写成矩阵形式为
\[ \begin{pmatrix} F_n\\ F_{n-1} \end{pmatrix} = \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} F_{n-1}\\ F_{n-2} \end{pmatrix} \]
于是可得
\[ \begin{aligned} \begin{pmatrix} F_n\\ F_{n-1} \end{pmatrix} &= \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} F_{n-1}\\ F_{n-2} \end{pmatrix} \\ &= \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}^2 \begin{pmatrix} F_{n-2}\\ F_{n-3} \end{pmatrix} \\ &= \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}^{n-2} \begin{pmatrix} 1\\ 1 \end{pmatrix} \\ &=\mathbf A^{n-2} \begin{pmatrix} 1\\ 1 \end{pmatrix} \end{aligned} \]
\(\mathbf A= \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}\)的特征值为\(\lambda_1 = \frac{1 - \sqrt 5}{2}\), \(\lambda_2 = \frac{1 + \sqrt 5}{2}\),对应的特征向量为 \(\vec x_1 = (\frac{1 - \sqrt 5}{2}, 1)^T\), \(\vec x_2 = (\frac{1 + \sqrt 5}{2}, 1)^T\)
特征值的求解方法
设\(\lambda\)是\(\mathbf A\)的特征向量,\(\vec x\)是对应的特征向量,则有
\[ \mathbf A \vec x = \lambda \vec x \]
从而得到\(\lambda \mathbf E - \mathbf A\)的秩为0.
\[ \left| \begin{matrix} \lambda - 1 & -1 \\ -1 & \lambda \end{matrix} \right| = 0 \Rightarrow \lambda ^2 - \lambda - 1 = 0 \]
从而得到两个解:\(\lambda_1 = \frac{1 - \sqrt 5}{2}\), \(\lambda_2 = \frac{1 + \sqrt 5}{2}\)
将\(\lambda_1, \lambda_2\)的值代入到\((\lambda \mathbf E - \mathbf A)\vec x = \vec 0\)可解得两个特征向量
\[ \vec x_1 = (\frac{1 - \sqrt 5}{2}, 1)^T \]
和
\[ \vec x_2 = (\frac{1 + \sqrt 5}{2}, 1)^T \]
因此\(\mathbf A\)是可对角化的,即令
\[ \mathbf P = \begin{pmatrix} \vec x_1, \vec x_2 \end{pmatrix} ,\]
则有\(\mathbf P^{-1} \mathbf A \mathbf P = \mathbf\Lambda=\begin{pmatrix} \lambda_1 & 0 \\ 0 & \lambda_2 \end{pmatrix}\) 因此\(\mathbf A^{n-2}=(\mathbf P^{-1} \mathbf\Lambda \mathbf P)^{n-2}=\mathbf P \mathbf\Lambda^{n-2} \mathbf P^{-1}\)
\[ \begin{aligned} \mathbf P \mathbf\Lambda^{n-2} \mathbf P^{-1} \begin{pmatrix} 1 \\ 1 \end{pmatrix} &= \frac{1}{\lambda_1 - \lambda_2} \begin{pmatrix} \lambda_1 & \lambda_2 \\ 1 & 1 \end{pmatrix} \begin{pmatrix} \lambda_1 & 0 \\ 0 & \lambda_2 \end{pmatrix}^{n-2} \begin{pmatrix} 1 & -\lambda_2 \\ -1 & \lambda_1 \end{pmatrix} \begin{pmatrix} 1 \\ 1 \end{pmatrix} \\ &=\frac{1}{\lambda_1 - \lambda_2} \begin{pmatrix} \lambda_1 & \lambda_2 \\ 1 & 1 \end{pmatrix} \begin{pmatrix} \lambda_1^{n-2} & 0 \\ 0 & \lambda_2^{n-2} \end{pmatrix} \begin{pmatrix} \lambda_1 \\ \lambda_2 \end{pmatrix}\\ &=\frac{1}{\lambda_1 - \lambda_2} \begin{pmatrix} \lambda_1 & \lambda_2 \\ 1 & 1 \end{pmatrix} \begin{pmatrix} \lambda_1^{n-1} \\ \lambda_2^{n-1} \end{pmatrix}\\ &=\frac{1}{\lambda_1 - \lambda_2} \begin{pmatrix} \lambda_1^n + \lambda_2^n\\ \lambda_1^{n-1}+\lambda_2^{n-1} \end{pmatrix}\\ \end{aligned} \]
即通项公式为\(F_n=\frac{1}{\lambda_1 - \lambda_2}(\lambda_1^n + \lambda_2^n)\),代入特征值得到\(F_n=\frac{1}{\sqrt 5}((\frac{1+\sqrt 5}{2})^n - (\frac{1 - \sqrt 5}{2})^n)\)
参考资料:斐波那契数列通项公式求解