ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LLM面试8大硬门槛:从Transformer到幻觉,答不上来直接淘汰

LLM面试8大硬门槛:从Transformer到幻觉,答不上来直接淘汰

1. 为什么这8个问题成了LLM面试的硬门槛

面过几十个LLM方向的候选人之后,我越来越确信一件事:这个领域的面试已经过了“你会不会调API”的阶段。现在面试官抛出的问题,几乎全部指向同一个核心——你到底有没有真正理解过大模型内部在发生什么。标题里说的“答不上来直接淘汰”,听起来有点狠,但站在面试官的角度想想:一个连Tokenization和Embedding区别都说不清的人,你怎么放心让他去调Prompt、做RAG、搭Agent?

这篇文章的由来,是我最近帮团队整理面试题库时,把过去一年问过的、真正能区分候选人水平的问题做了系统梳理。最终收敛到8个方向:Transformer架构理解、Tokenization机制、Embedding原理与应用、注意力机制细节、Function Calling设计、模型微调策略、推理优化、以及幻觉与可靠性问题。这8个问题覆盖了从底层原理到工程落地的完整链路,任何一个答不上来,基本可以判断候选人在这个方向上缺乏深度实践。

适合谁来读?如果你正在准备LLM方向的面试,不管是算法岗还是应用工程岗,这篇内容可以当作一份自检清单。如果你是面试官,也可以直接拿去用,每个问题我都附上了“及格线”和“加分项”的区分标准。如果你只是对LLM感兴趣的技术人,这里面的原理拆解和实操经验同样能帮你建立起一套完整的认知框架。

下面我逐个拆解,每个问题都会说清楚:面试官到底想考什么、你应该怎么回答、以及我在实际面试中见过的典型翻车现场。

2. Transformer架构:不只是“Attention is All You Need”

2.1 面试官真正想考的是什么

“介绍一下Transformer”这个问题,几乎每场面试都会出现。但面试官想听的绝对不是你把论文摘要背一遍。我通常会在候选人讲完标准答案之后,追问三个问题:为什么用LayerNorm而不是BatchNorm?为什么是多头而不是单头?位置编码为什么用正弦函数而不是可学习参数?

这三个追问能直接区分出“看过论文”和“真正理解设计动机”的人。大部分候选人能说出“多头可以捕捉不同子空间的特征”,但当我问“那为什么不是越多头越好”时,就卡住了。实际上头的数量受限于d_model,每个头的维度是d_model / h,头太多会导致每个头的维度太小,表达能力反而下降。这个细节在原始论文里没有明说,但在实际调参时非常关键。

2.2 编码器与解码器的结构差异

很多人把Transformer当成一个整体来讲,但面试中你需要清晰地拆开说。编码器是双向注意力,每个位置可以看到全序列;解码器是因果注意力,每个位置只能看到自己和之前的位置。这个差异直接决定了它们适合的任务类型。

我一般会这样解释:编码器像一个“全局审阅者”,它需要看到完整上下文才能理解每个词的含义,适合分类、抽取、Embedding生成等任务。解码器像一个“逐字写作的人”,它只能基于已写的内容决定下一个词,适合文本生成。而Encoder-Decoder结构(比如T5、BART)则是“先读懂再写”,适合翻译、摘要这类需要理解输入再生成输出的任务。

实际面试中,我会让候选人画一下三种结构的注意力矩阵形状。这个看似简单的问题,能直接暴露对方是否真正动手实现过Transformer。

2.3 残差连接与层归一化的工程考量

残差连接看起来简单,但它在深层Transformer中的作用远超“防止梯度消失”。我试过在24层以上的模型中移除残差连接,训练直接崩掉,loss完全不下降。原因是残差连接保证了梯度可以绕过非线性变换直接回传,这对于深层网络的稳定训练是必须的。

LayerNorm的位置也有讲究。原始论文用的是Post-LN(先做注意力/FFN,再归一化),但后来GPT-2开始改用Pre-LN(先归一化,再做注意力/FFN)。Pre-LN的训练更稳定,不需要warmup就能收敛,但最终效果可能略逊于精心调参的Post-LN。这个取舍在实际项目中非常实用——如果你训练资源有限,Pre-LN是更安全的选择。

实操心得:如果你在面试中被问到“训练不收敛怎么办”,优先检查LayerNorm的位置和初始化方式。我踩过的坑是:Post-LN结构下,如果不做learning rate warmup,前几千步的梯度范数会爆炸。

2.4 位置编码的选型逻辑

正弦位置编码的优势在于它可以外推到训练时没见过的序列长度。但在实际使用中,外推效果往往不如预期。后来RoPE(旋转位置编码)成为主流,因为它通过旋转矩阵编码相对位置信息,在长序列上表现更好。现在很多开源模型(LLaMA、Qwen等)都用RoPE。

面试中如果候选人能说出“RoPE通过绝对位置编码的方式实现了相对位置编码的效果”,我会给加分。如果再能解释清楚RoPE的远程衰减特性——即随着距离增大,注意力分数自然衰减——那就是资深级别的理解了。

3. Tokenization:大模型的第一道关卡

3.1 为什么Tokenization值得单独问

很多人觉得Tokenization只是预处理,不重要。但实际项目中,Tokenization直接影响到模型的效果、推理成本、甚至多语言能力。我见过一个真实案例:某个团队做中文法律文书摘要,模型效果一直不好,排查了很久才发现是Tokenizer把很多法律术语切碎了,导致模型根本学不到完整的术语表示。

面试中我会问:“BPE、WordPiece、SentencePiece有什么区别?各自适合什么场景?”这个问题能直接看出候选人有没有处理过真实数据。

3.2 BPE、WordPiece与SentencePiece的核心差异

BPE(Byte Pair Encoding)的思路是:从字符开始,不断合并出现频率最高的相邻对。它的优点是简单、通用,缺点是合并规则是基于频率的,可能产生不合理的子词。GPT系列用的是BPE的变体。

WordPiece是BERT用的方法,它和BPE的区别在于:BPE选频率最高的对,WordPiece选能最大化语言模型似然的对。实际效果上,WordPiece更倾向于产生有语言学意义的子词。

SentencePiece的最大特点是它把输入当作原始字节流处理,不依赖空格分词。这对中文、日文等没有空格分隔的语言非常重要。LLaMA系列用的就是SentencePiece。

方法核心思路代表模型适合场景
BPE合并最高频相邻对GPT系列英文为主,通用场景
WordPiece最大化语言模型似然BERT需要语言学合理性
SentencePiece字节流处理,不依赖空格LLaMA、T5多语言、中文场景

3.3 词表大小的取舍

词表大小是一个经典的工程取舍。词表太小,一个词被切成很多token,序列变长,推理变慢;词表太大,Embedding矩阵参数量增加,低频词训练不充分。常见的词表大小在32K到128K之间。LLaMA-2用的是32K,而一些中文模型会扩展到64K甚至100K。

我在实际项目中的经验是:如果你的应用场景以中文为主,词表至少要到50K以上,否则很多常用词会被切碎。你可以用一个简单的方法验证:拿一批业务数据,统计平均每个词被切成几个token。如果超过2个,说明词表可能偏小。

3.4 特殊Token的设计与使用

面试中很少有人主动提到特殊Token,但这恰恰是实际工程中容易出问题的地方。[CLS]、[SEP]、[PAD]、[UNK]这些是基础,但在对话模型中,<|system|>、<|user|>、<|assistant|>这些角色标记的设计直接影响模型的行为。

我踩过的一个坑:在微调对话模型时,没有正确设置<|assistant|>标记的loss mask,导致模型把用户的问题也当作需要生成的内容来学习,结果模型开始“自问自答”。正确的做法是只在assistant回复的部分计算loss,其他部分mask掉。

注意事项:不同模型的特殊Token不同,做微调时一定要查清楚基座模型的Token配置。用错Token会导致模型行为完全异常,而且很难排查。

4. Embedding:从词向量到语义搜索的基石

4.1 Embedding的本质是什么

Embedding就是把离散的符号映射到连续的向量空间。但面试中我会追问:“为什么Embedding能表达语义相似性?”这个问题的核心在于分布式假设:出现在相似上下文中的词,含义相似。训练过程中,模型通过预测上下文任务,自然地把语义相近的词推到一起。

但要注意,Transformer产生的Embedding和Word2Vec的Embedding有本质区别。Word2Vec是静态的,每个词只有一个向量;Transformer是动态的,同一个词在不同上下文中会有不同的向量。这就是为什么BERT的Embedding在语义任务上远优于Word2Vec。

4.2 如何选择Embedding模型

现在Embedding模型排行上常见的选项包括:OpenAI的text-embedding-3系列、BGE系列、GTE系列、E5系列等。选择时需要考虑几个维度:维度大小、最大输入长度、多语言支持、推理速度、以及是否支持指令前缀。

我的经验是:如果做中文语义搜索,BGE-large-zh-v1.5是一个很稳的选择,维度1024,最大长度512。如果需要多语言,BGE-M3支持100+语言,而且同时支持稠密检索、稀疏检索和多向量检索。如果追求极致速度,可以用BGE-small或GTE-small,维度降到384或512,效果下降在可接受范围内。

模型维度最大长度多语言适合场景
BGE-large-zh1024512中文中文语义搜索
BGE-M310248192100+多语言、长文本
GTE-large1024512英文为主英文语义搜索
text-embedding-3-small15368191多语言通用场景

4.3 向量检索的实操细节

拿到Embedding之后,下一步是向量检索。很多人直接用余弦相似度做暴力搜索,数据量小的时候没问题,但超过10万条就需要用近似最近邻(ANN)算法。常用的库包括Faiss、Annoy、HNSW等。

我实测下来,HNSW在召回率和速度之间平衡得最好。关键参数是M(每个节点的连接数)和efConstruction(构建时的搜索深度)。M越大,召回率越高,但内存占用也越大。一般M设为16-64,efConstruction设为100-200。

还有一个容易忽略的点:归一化。如果你用余弦相似度,一定要先把向量归一化到单位长度,否则内积和余弦相似度不等价。我见过有人忘了归一化,导致检索结果完全不对。

4.4 Embedding微调的必要性与方法

通用Embedding模型在垂直领域往往表现不佳。比如医疗、法律、金融这些领域,术语的语义和通用领域差异很大。这时候就需要微调Embedding模型。

微调Embedding通常用对比学习:构造正样本对(语义相似的句子)和负样本对(语义不相似的句子),用InfoNCE损失训练。正样本可以从业务数据中挖掘,比如同一篇文档的不同段落、同一问题的不同表述。负样本可以用in-batch negatives,即同一个batch内其他样本作为负例。

我做过一个法律领域的Embedding微调,用2000对法律问答数据训练了3个epoch,检索准确率从62%提升到了81%。数据量不需要很大,但质量一定要高。

5. 注意力机制:面试中的分水岭

5.1 自注意力的计算过程

自注意力的核心公式是Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) V。面试中我会让候选人逐步解释这个公式的每个部分。QK^T计算的是每对位置之间的相似度,除以sqrt(d_k)是为了防止点积过大导致softmax梯度消失,softmax把相似度转成概率分布,最后用这些概率对V加权求和。

这个解释大部分人能说出来。但我会接着问:“为什么要除以sqrt(d_k)而不是d_k?”这个问题的答案在于:当d_k较大时,点积的方差会增大,导致softmax的输出趋近于one-hot,梯度变得极小。除以sqrt(d_k)可以把方差拉回到1附近,保持梯度的稳定性。这个细节在论文里有提,但很多人看的时候跳过了。

5.2 多头注意力的并行计算

多头注意力的实现方式是把d_model分成h份,每份维度是d_model/h,然后并行做h次注意力计算,最后拼接起来再做一次线性变换。这样做的好处是:不同的头可以关注不同的模式,有的头关注语法关系,有的头关注语义相似性,有的头关注位置邻近性。

实际实现中,我们不会真的创建h个独立的Q、K、V矩阵,而是用一个大的线性层把d_model映射到3 * d_model,然后reshape成h个头。这样可以用矩阵乘法一次性完成所有头的计算,效率更高。

5.3 注意力变体:从稀疏注意力到Flash Attention

标准注意力的计算复杂度是O(n^2),序列长度增加时计算量爆炸。为了解决这个问题,出现了很多变体:稀疏注意力只计算部分位置对,线性注意力用核函数近似softmax,Flash Attention通过分块计算和重计算减少显存占用。

Flash Attention是目前最实用的优化。它不改变注意力的数学结果,但通过tiling技术把注意力计算分块,避免了一次性存储完整的n x n注意力矩阵。我实测下来,Flash Attention可以把长序列训练的显存占用降低50%以上,速度提升2-3倍。

面试中如果候选人能说出“Flash Attention的核心是IO感知,它把计算分成小块,在SRAM中完成,减少HBM的读写”,那就是加分项。

5.4 注意力可视化与可解释性

注意力权重可以用于分析模型的行为。比如在翻译任务中,你可以可视化源语言和目标语言之间的注意力矩阵,看看模型是否正确对齐了词。但要注意,注意力权重不等于解释。有研究表明,注意力权重和梯度的重要性并不总是一致。所以用注意力做可解释性分析时,要谨慎下结论。

6. Function Calling:从对话到行动的桥梁

6.1 Function Calling解决了什么问题

大模型本身只能生成文本,不能查天气、不能发邮件、不能操作数据库。Function Calling就是让模型能够“调用外部工具”的机制。它的核心思路是:模型根据用户的意图,生成一个结构化的函数调用请求(通常是JSON格式),应用程序执行这个函数,把结果返回给模型,模型再基于结果生成最终回复。

这个机制让LLM从“聊天机器人”变成了“智能体”。面试中我会问:“Function Calling和传统的意图识别+槽位填充有什么区别?”核心区别在于:传统方法需要预先定义所有意图和槽位,扩展性差;Function Calling让模型自己决定调用哪个函数、传什么参数,灵活得多。

6.2 函数定义的设计要点

函数定义的质量直接影响调用准确率。我总结了几条经验:

  • 函数名要语义清晰:get_weather比func1好得多。
  • 参数描述要详细:包括类型、取值范围、是否必填、默认值。
  • 枚举值要列全:如果参数是枚举类型,把所有可能的值列出来。
  • 避免函数功能重叠:如果两个函数功能相似,模型容易混淆。

一个常见的坑是:参数描述太模糊,导致模型传错参数。比如date参数,如果不说明格式,模型可能传2024-01-01,也可能传Jan 1, 2024。明确写“格式为YYYY-MM-DD”可以大幅降低错误率。

6.3 多轮函数调用的处理

复杂任务往往需要多轮函数调用。比如用户说“帮我订明天从北京到上海的机票,然后加到日历里”,模型需要先调用search_flights,拿到结果后调用book_flight,最后调用add_calendar_event。

处理多轮调用时,关键是要维护好对话状态。每次函数调用的结果都要追加到对话历史中,让模型知道上一步做了什么。同时要设置最大调用轮数,防止模型陷入无限循环。

实操心得:我建议在系统提示中明确告诉模型“如果函数调用失败,尝试其他方式或告知用户”,否则模型可能会反复调用同一个失败的函数。

6.4 错误处理与容错设计

Function Calling的错误处理是工程中的难点。常见的错误包括:函数不存在、参数格式错误、函数执行超时、返回结果为空。每种错误都需要不同的处理策略。

我的做法是:在函数执行层做参数校验,如果参数不合法,返回一个结构化的错误信息给模型,让模型重新生成。如果函数执行超时,返回超时提示,让模型决定是重试还是告知用户。如果函数不存在,直接告诉模型“该函数不可用”,避免模型反复尝试。

这种容错设计是构建可靠AI系统的关键。面试中如果候选人能主动提到错误处理,我会认为他有实际工程经验。

7. 模型微调:从通用到专用的必经之路

7.1 全量微调、LoRA与QLoRA的取舍

全量微调更新所有参数,效果最好,但显存需求大。一个7B模型全量微调需要至少4张A100 80G。LoRA只训练低秩矩阵,显存需求降低到1/3左右,效果接近全量微调。QLoRA在LoRA基础上把基座模型量化到4-bit,显存需求进一步降低,单张24G显卡就能微调7B模型。

选择哪种方式取决于你的资源和需求。如果数据量很大(百万级),全量微调值得投入。如果数据量在几千到几万条,LoRA足够。如果资源极其有限,QLoRA是唯一选择。

方法显存需求(7B)训练速度效果适合场景
全量微调4x80G慢最好大数据量、追求极致效果
LoRA1x40G快接近全量中小数据量、快速迭代
QLoRA1x24G较慢略逊于LoRA资源受限

7.2 数据质量比数量更重要

我做过一个对比实验:用1000条高质量数据和10000条低质量数据分别微调,结果1000条高质量数据的效果明显更好。高质量数据的标准是:格式统一、答案准确、覆盖多样、无重复。

数据标注是另一个关键环节。LLM的数据标注和传统NLP不同,不需要标注每个token的标签,而是需要构造高质量的输入-输出对。我的经验是:让标注人员先写一个“理想回答”,然后让模型生成几个回答,标注人员从中选择最好的并修改。这样效率比从零写高得多。

7.3 微调中的过拟合与灾难性遗忘

微调最容易出现的问题是过拟合和灾难性遗忘。过拟合表现为:训练loss持续下降,但验证loss开始上升。灾难性遗忘表现为:模型在通用任务上的能力下降。

解决过拟合的方法包括:早停、数据增强、Dropout、权重衰减。解决灾难性遗忘的方法包括:混合通用数据一起训练、使用LoRA只更新部分参数、降低学习率。

我通常会在微调数据中混入10%-20%的通用指令数据,这样可以在保持专用能力的同时,减缓通用能力的下降。

8. 推理优化:让模型跑得更快更省

8.1 KV Cache的原理与优化

自回归生成时,每生成一个token都需要重新计算所有之前token的Key和Value。KV Cache把这些中间结果缓存起来,避免重复计算。没有KV Cache时,生成n个token的计算量是O(n^2);有了KV Cache,计算量降到O(n)。

但KV Cache会占用大量显存。对于一个7B模型,序列长度2048,batch size 1,KV Cache大约占用1.5GB。如果batch size增大或序列变长,显存占用会线性增长。优化方法包括:MQA(Multi-Query Attention)让所有头共享K和V,GQA(Grouped-Query Attention)让每组头共享K和V。LLaMA-2 70B用的就是GQA。

8.2 量化推理的实操

量化是把模型参数从FP16降到INT8或INT4,减少显存占用和加速推理。常用的量化方法包括GPTQ、AWQ、GGUF等。GGUF格式特别适合在消费级硬件上运行,支持CPU和GPU混合推理。

我实测下来,7B模型用Q4_K_M量化后,显存占用从14GB降到4GB左右,推理速度提升约1.5倍,效果下降在可接受范围内。如果追求更高精度,可以用Q5或Q8量化,但显存占用和速度会相应变化。

注意事项:量化后的模型在某些任务上可能出现明显的质量下降,特别是需要精确计算或长链推理的任务。建议在量化后做一轮评估,确认关键指标没有大幅下降。

8.3 批处理与连续批处理

批处理可以提升GPU利用率,但传统的静态批处理需要等最长的序列生成完才能返回结果。连续批处理(Continuous Batching)允许新请求动态加入正在运行的batch,大幅提升吞吐量。vLLM和TensorRT-LLM都支持连续批处理。

我实测过vLLM和HuggingFace Transformers的吞吐量对比:在同样的硬件上,vLLM的吞吐量是Transformers的5-10倍。这个差距主要来自PagedAttention和连续批处理。

9. 幻觉与可靠性:LLM落地的最后一道坎

9.1 幻觉的成因分析

幻觉是指模型生成了看似合理但实际错误的内容。成因包括:训练数据中的错误信息、模型过度泛化、解码策略过于激进、以及模型缺乏事实核查能力。

面试中我会问:“如何减少幻觉?”常见的回答包括RAG、温度调低、提示词约束。但我会追问:“RAG能完全解决幻觉吗?”答案是:不能。RAG只能提供正确的参考信息,但模型仍然可能忽略或错误解读这些信息。所以RAG之后还需要做引用验证和一致性检查。

9.2 RAG系统中的可靠性设计

RAG的可靠性取决于检索质量和生成质量。检索方面,要确保召回的文档确实相关。我通常会用交叉编码器做重排序,把检索到的Top-20文档重新打分,选出Top-5最相关的。生成方面,要约束模型只基于检索到的内容回答,如果检索结果不包含答案,模型应该明确说“不知道”。

一个实用的技巧是:在提示词中要求模型标注每个事实的来源。比如“根据文档[1],...”。这样用户可以验证信息的可靠性,也方便排查错误。

9.3 评估与监控

LLM应用的评估比传统软件复杂得多,因为输出是开放式的。我通常从几个维度评估:准确性(事实是否正确)、相关性(是否回答了问题)、完整性(是否覆盖了关键点)、安全性(是否有害内容)。

自动化评估可以用LLM-as-Judge,让一个更强的模型来打分。但要注意,LLM-as-Judge也有偏差,比如倾向于给更长的回答打高分。所以最好结合人工评估,定期抽样检查。

监控方面,要记录每次请求的输入、输出、检索结果、函数调用记录。当用户反馈问题时,可以快速定位是检索问题、生成问题还是函数调用问题。

10. 面试中的加分项与翻车现场

10.1 什么样的回答能让面试官眼前一亮

根据我面试的经验,加分项通常包括:主动提到工程中的取舍和权衡、能说出自己踩过的坑和解决方法、对最新技术保持关注、以及能清晰地解释复杂概念。

比如问到Embedding时,如果候选人能说出“BGE-M3支持稠密、稀疏和多向量三种检索方式,我在项目中用稠密检索做粗排,稀疏检索做精确匹配”,这就是有实际经验的体现。问到推理优化时,如果能说出“vLLM的PagedAttention把KV Cache分页管理,减少了显存碎片”,说明确实动手部署过。

10.2 常见的翻车回答

翻车回答通常有几类:只背概念不理解原理、过度自信但细节错误、以及完全缺乏工程思维。

我印象最深的一个候选人,问到“Transformer为什么比RNN好”时,他说“因为Transformer可以并行计算”。这个回答本身没错,但当我追问“那RNN为什么不能并行”时,他答不上来。实际上RNN的隐藏状态依赖前一步的计算结果,天然是串行的。而Transformer的注意力机制可以一次性看到所有位置,所以可以并行。这个追问直接暴露了他只是记住了结论,没有理解本质。

另一类翻车是过度自信。有个候选人在回答“如何减少幻觉”时,斩钉截铁地说“用RAG就能解决”。当我追问“如果检索到的文档本身有错误怎么办”时,他愣住了。实际上RAG只是把问题从“模型不知道”变成了“模型基于错误信息回答”,并没有从根本上解决可靠性问题。

10.3 如何准备这8个问题

我的建议是:不要死记硬背答案,而是动手做一遍。自己实现一个简单的Transformer,跑一遍Tokenization,训练一个小的Embedding模型,搭一个RAG系统,部署一次vLLM。这些实操经验会让你在面试中的回答有血有肉,而不是干巴巴的概念。

另外,准备几个自己项目中的案例。面试官最喜欢听的是“我在项目中遇到了什么问题,我是怎么分析和解决的”。这种故事比任何标准答案都有说服力。

最后再分享一个小技巧:面试中遇到不会的问题,不要硬编。可以说“这个我没有深入研究过,但根据我的理解,可能是...”。面试官更看重你的思考过程,而不是标准答案。诚实和逻辑清晰,比假装什么都懂要好得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进