来源:AIGC小白入门记

最近参加了智谱大模型算法岗位的面试,收获颇丰,特地来分享一下这次的面试经历,希望能给正在求职的小伙伴们提供一些参考。

一、面试流程

面试整体分为三个部分:手撕代码、项目介绍以及专业知识问答(八股)。

(一)手撕代码

面试官首先让我手撕一个 tokenizer,tokenizer 是自然语言处理中用于将文本拆分成一个个标记(token)的工具。我从最简单的字符级别 tokenizer 开始讲起,然后逐步扩展到基于词典的分词方法,最后还提到了一些基于深度学习的预训练分词模型,如 BERT 的 WordPiece 算法。面试官对细节把握得很严,让我详细解释了如何处理一些特殊字符、未知词汇等问题;

还让我用伪代码简单描述了 RAG(Retrieval-Augmented Generation)模型的基本流程。RAG 是一种结合了检索和生成的模型,先从一个大规模的文档集合中检索出与输入文本相关的文档片段,然后再基于这些片段生成目标文本。我用伪代码展示了检索部分和生成部分的交互过程,面试官对这个部分的逻辑和实现细节很感兴趣,问了我一些优化思路。

(二)项目介绍

项目介绍部分是面试的重点之一,面试官让我详细讲解我之前参与的一个项目。

  1. 项目具体解决问题:我介绍的项目是一个智能问答系统,主要解决的是用户在海量数据中快速获取准确信息的问题。例如,用户可以通过自然语言提问,系统能够理解问题并从数据库中检索出相关的答案。

  2. 每个模块如何实现:这个项目分为多个模块,包括自然语言理解(NLU)、知识库管理、问答匹配和答案生成等模块。在 NLU 模块,我详细讲解了如何使用深度学习模型(如 LSTM)来提取问题的语义特征;在知识库管理模块,我介绍了如何构建和维护一个结构化的知识库,以支持高效的检索;在问答匹配模块,我讲述了如何通过计算问题和知识库中条目的相似度来找到最相关的答案;在答案生成模块,我提到了使用模板生成和基于生成模型(如 GPT)生成两种策略。

  3. NL2SQL 在处理多表查询的时候采取啥策略:这个项目中用到了 NL2SQL(自然语言到结构化查询语言)技术,用于将用户的自然语言问题转换为 SQL 查询语句。在处理多表查询时,我采用了基于语义解析的方法,首先解析出问题中涉及的实体、关系和属性,然后根据这些信息构建出多表连接的 SQL 查询。例如,对于一个涉及两个表(如“学生表”和“课程表”)的查询问题,我会先解析出学生和课程之间的关系(如“选课”关系),然后生成相应的 JOIN 语句来连接这两个表。

  4. k8s 和 docker 在项目中的作用:在项目的部署阶段,我们使用了 Docker 来容器化每个模块,这样可以保证每个模块的运行环境一致,避免了“在我的机器上可以运行”的问题。而 Kubernetes(k8s)则用于管理这些容器,实现自动扩缩容、负载均衡等功能。例如,当问答系统的访问量突然增加时,k8s 可以自动启动更多的容器实例来应对高流量,保证系统的稳定运行。

(三)专业知识问答(八股)

这部分主要考察我对一些常见算法、模型和框架的理解。

  1. 对 PPO、DPO、GRPO 计算逻辑的理解:PPO(Proximal Policy Optimization)是一种强化学习算法,通过截断概率比来限制策略更新的幅度,从而保证更新的稳定性。DPO(Deep Policy Optimization)是 PPO 的一种改进版本,引入了深度学习的思想,可以更好地处理复杂的策略函数。GRPO(Generalized Reinforcement Policy Optimization)则是一种更通用的框架,可以涵盖多种强化学习算法。我详细解释了它们的计算逻辑,包括如何计算目标函数、如何更新策略等。

  2. 对 KL 散度的三种估计的理解:KL 散度(Kullback–Leibler Divergence)是衡量两个概率分布差异的指标。常见的估计方法有直接计算法、蒙特卡洛估计法和变分估计法。直接计算法需要知道两个分布的解析形式,蒙特卡洛估计法通过采样来近似计算 KL 散度,而变分估计法则通过引入一个变分分布来降低计算复杂度。我对比了这三种方法的优缺点,例如直接计算法精度高但适用范围有限,蒙特卡洛估计法简单但可能需要大量样本,变分估计法可以用于复杂的分布但可能存在偏差。

  3. LORA 和 p-tuning v2 的区别,怎么初始化:LORA(Low-Rank Adaptation)是一种参数高效的微调方法,通过在预训练模型的每一层插入低秩矩阵来实现微调。p-tuning v2 是一种基于提示的学习方法,通过在输入中添加提示来引导模型生成期望的输出。它们的主要区别在于 LORA 是直接修改模型的参数,而 p-tuning v2 是通过改变输入的形式来影响模型的输出。在初始化方面,LORA 的低秩矩阵通常初始化为小的随机值,而 p-tuning v2 的提示可以通过一些启发式方法来设计,例如根据任务的语义来选择合适的提示词。

  4. DeepSeek R1 训练全流程,MLA 具体怎么做的:DeepSeek R1 是一个大规模的预训练模型,其训练全流程包括数据预处理、模型初始化、训练迭代和模型评估等步骤。在数据预处理阶段,需要对海量的文本数据进行清洗、分词和编码等操作;模型初始化时,会随机初始化模型的参数;在训练迭代过程中,使用大规模的计算资源进行分布式训练,通过反向传播更新模型参数;最后通过一些指标(如困惑度、准确率等)来评估模型的性能。MLA(Multi-Label Attention)是 DeepSeek R1 中用于处理多标签分类任务的一种机制,它通过引入多个注意力头来分别关注不同的标签信息,从而提高多标签分类的性能。

  5. 梯度爆炸和消失怎么处理的:梯度爆炸和消失是深度学习训练过程中常见的问题。对于梯度爆炸,可以采用梯度裁剪的方法,将梯度限制在一个合理的范围内;对于梯度消失,可以通过使用合适的激活函数(如 ReLU 及其变体)、初始化方法(如 Xavier 初始化)和网络结构(如残差网络)来缓解。我详细解释了这些方法的原理和应用场景。

  6. torch 中 register parameter 和 buffer 的区别:在 PyTorch 中,register_parameter用于注册模型的参数,这些参数会被自动加入到模型的参数列表中,会在训练过程中被优化器更新。而register_buffer用于注册一些不需要更新的张量,例如模型的输入数据、中间变量等,这些张量不会被优化器更新,但会在模型的前向传播过程中被使用。我通过一个简单的例子来说明它们的区别。

  7. torch 如何实现不记录梯度:在 PyTorch 中,可以通过设置torch.no_grad()上下文管理器来实现不记录梯度。在torch.no_grad()的作用范围内,所有的操作都不会记录梯度,这在推理阶段或者一些不需要计算梯度的场景中非常有用。例如,在使用预训练模型进行推理时,可以使用torch.no_grad()来提高计算效率。

  8. torch squeeze 和 unsqueeze 干嘛的torch.squeezetorch.unsqueeze是 PyTorch 中用于操作张量维度的函数。torch.squeeze用于去除张量中大小为 1 的维度,例如将一个形状为[1, 3, 1, 4]的张量压缩为[3, 4];而torch.unsqueeze则用于在张量的指定位置插入一个大小为 1 的维度,例如将一个形状为[3, 4]的张量扩展为[1, 3, 4]。这两个函数在处理张量的维度时非常方便,尤其是在进行张量拼接、广播等操作时。

  9. python 设计删除流程的时候,若涉及可变和不可变对象该如何 debug:在 Python 中,可变对象(如列表、字典等)和不可变对象(如整数、字符串等)在删除操作时的行为有所不同。对于可变对象,直接修改对象的内容会影响所有引用该对象的变量;而对于不可变对象,删除操作只是让变量不再指向该对象,不会影响其他变量。在设计删除流程时,需要注意这些区别,避免出现意外的错误。调试时可以通过打印变量的引用地址(使用id()函数)来检查变量是否指向同一个对象,从而确定删除操作是否按预期执行。

  10. 多机多卡和单机多卡的实现上有什么不同:多机多卡和单机多卡都是用于加速深度学习模型训练的分布式计算方式。单机多卡主要通过

春天的美妙时光

LEARN FROM LEI FENG