└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | # advanced_LLM_interview_notes
2 | 收集了一些进阶的大模型面经和相关知识,持续更新,来源均注明出处
3 |
4 | ## RLHF
5 | https://github.com/modelscope/modelscope-classroom/blob/main/LLM-tutorial/M.%E4%BA%BA%E7%B1%BB%E5%81%8F%E5%A5%BD%E5%AF%B9%E9%BD%90%E8%AE%AD%E7%BB%83.md
6 |
7 | ### PPO
8 | https://zhuanlan.zhihu.com/p/677607581
9 |
10 | ### DPO
11 | https://zhuanlan.zhihu.com/p/642569664
12 |
13 | ### KTO
14 | https://zhuanlan.zhihu.com/p/695992165x
15 |
16 | 对正负样本进行了加权;DPO里面是使用正负样本的reward差值进行sigmoid映射,但是KTO里面使用reward模型与KL散度之间的差异
17 |
18 | ### SimPO
19 | https://www.thepaper.cn/newsDetail_forward_27513961
20 |
21 | 采用了与生成指标直接对齐的隐式奖励形式,从而消除了对参考模型的需求。此外,其还引入了一个目标奖励差额 γ 来分离获胜和失败响应
22 |
23 | ### GRPO
24 | https://zhuanlan.zhihu.com/p/20565045592
25 |
26 | ### DAPO
27 | https://zhuanlan.zhihu.com/p/696537369
28 |
29 | 相较于GRPO
30 | + 去掉了KL散度惩罚
31 | + clip的上下界不一样
32 | + GRPO是对每个回答的奖励除以回答中token的数量,然后在不同回答间做一个平均,而DAPO是对一个问题的所有回答的所有token的奖励求和,再除以总的token做一个平均
33 | + 正确答案的数量大于0且小于G,也就是G个回答不能没有正确答案,也不能全都是正确答案
34 | + 惩罚过长的回答
35 | + 不用模型来判断是否给予奖励,而是设置奖励机制判断答案是否正确来得到奖励,防止reward hacking
36 |
37 | ### Reward Model是怎么训练的
38 | https://zhuanlan.zhihu.com/p/595579042
39 |
40 | ## Advanced RAG
41 | + 分层索引检索,利用文档摘要创建多层索引,优先检索与查询最相关的摘要部分,再深入到详细文档,提高检索效率
42 | + query改写
43 | + 使用假设文档嵌入修正查询与文档的非对称性 (HyDE)
44 | + 在检索前,生成一个与用户查询相关的假设文档,并使用这个文档的嵌入来替代用户的查询进行语义搜索
45 | + 相关文档重排或者直接用LLM打分
46 | + 压缩搜索结果
47 | + 加入反思
48 |
49 | ## LLM量化
50 | https://github.com/modelscope/modelscope-classroom/blob/main/LLM-tutorial/G.%E9%87%8F%E5%8C%96.md
51 |
52 | 按照量化发生的步骤区分,可以划分为PTQ(训练后量化,或离线量化)和QAT(训练感知型量化,或在线量化)。
53 | PTQ量化可以分为data-free和calibration两种,前者不使用数据集进行校准直接计算量化因子,后者会根据少量真实数据进行统计分析并对量化因子进行额外校准,但耗费的时间更长。
54 | QAT量化会先在待量化的算子上增加一个伪量化结构,并在训练时模拟量化过程并实时更新计算量化因子(类似反向传播过程)及原始权重。
55 |
56 | 按照量化方法可以划分为线性量化、非线性量化(如对数量化)等多种方式,目前较为常用的是线性量化。
57 | 其中线性量化又可以按照对称性划分为对称量化和非对称量化,非对称量化为了解决weight分布不均匀问题,其在公式中增加了zero_point项:qweight=round(weight/scale + zero_point),使稠密数据部分可以得到更宽泛的数值范围。
58 |
59 | 按照量化粒度划分可以分为**逐层量化(每层使用一套量化因子)、逐组量化(在每层中按照group使用一套量化因子)、逐通道量化(按channel划分量化因子)**等几种方式。
60 |
61 | ### LLM.int8()
62 | 从输入的隐含状态中,按列提取异常值 (离群特征,即大于某个阈值的值)
63 |
64 | 对离群特征进行 FP16 矩阵运算,对非离群特征进行量化,做 INT8 矩阵运算
65 |
66 | 反量化非离群值的矩阵乘结果,并与离群值矩阵乘结果相加,获得最终的 FP16 结果
67 |
68 | 拖慢了推理速度
69 |
70 | ### GPTQ
71 | 采用 W4A16 的混合量化方案,其中模型权重被量化为 int4 数值类型,而激活值则保留在 float16,是一种仅权重量化方法
72 |
73 | 在推理阶段,模型权重被动态地反量化回 float16 并在该数值类型下进行实际的运算
74 |
75 | GPTQ还是从单层量化的角度考虑,希望找到一个量化过的权重,使的新的权重和老的权重之间输出的结果差别最小
76 | GPTQ 将权重分组(如:128列为一组)为多个子矩阵(block)。对某个 block 内的所有参数逐个量化,每个参数量化后,需要适当调整这个 block 内其他未量化的参数,以弥补量化造成的精度损失。因此,GPTQ 量化需要准备校准数据集。
77 |
78 | ### SmoothQuant
79 | 训练后量化 (PTQ) 方法, W8A8 量化。
80 | 由于权重很容易量化,而激活则较难量化,因此,SmoothQuant 引入平滑因子s来平滑激活异常值,通过数学上等效的变换将量化难度从激活转移到权重上。
81 | AWQ
82 | 仅权重量化方法。通过保护更“重要”的权重不进行量化,从而在不进行训练的情况下提高准确率。只保留 0.1%-1% 的较大激活对应权重通道为 FP16 。
83 |
84 | ## LLM+tools
85 | ### Toolformer
86 | 通过in-context learning的方式从训练数据中采样出包含工具调用的数据
87 |
88 | 如果提供API和对应执行结果后生成答案的LM loss比啥也不提供的LM loss小一个阈值,那就认为这个API是有帮助的
89 |
90 | ### ToolLLM
91 | 从Rapidapi收集了一批API,当API响应结果长度超过2048个token,研究人员将tool的相关信息以及3个压缩样例融入prompt中,通过ChatGPT对响应结果进行压缩,如果压缩后的结果长度依旧超过限制,那就保留前面的2048个token。
92 |
93 | 构建了一个(Instruction, APIs)的数据集,一个(Instruction, solution)数据集。用(Instruction, APIs)数据集训练的API Retriever,用(Instruction, solution)数据集微调的LLaMa,LLaMa在根据Instruction寻找相应的API的时候调用BERT来进行API Retrieval
94 |
95 | ## LLama3.1中的scaling law
96 | https://www.bilibili.com/video/BV1Q4421Z7Tj/?spm_id_from=333.999.top_right_bar_window_dynamic.content.click&vd_source=e78a159ff471c8c0cb1842c33e4b7879
97 |
98 | 固定算力消耗(时间*卡数)下,计算不同模型大小在不同训练数据量上(此消彼长)的validation loss,找到最优点,即最佳的模型大小和训练数据量
99 |
100 | 对不同的算力消耗都按上述步骤做一遍,找到最优点
101 |
102 | 根据这些最优点可以知道,针对不同算力消耗所需要的最佳训练数据量
103 |
104 | 还有一种scaling law是针对benchmark的,计算Negative Log-Likelihood Loss和acc之间的关系
105 |
106 | ## LLM长度外推
107 | https://mp.weixin.qq.com/s/54YdSdB1uX-i7mt7kasFIQ
108 | ### 线性插值
109 | 对于向量的所有分组不加区分地缩小旋转弧度,降低旋转速度(进一步体现为对其正弦函数进行拉伸),会导致模型的高频信息缺失,从而影响模型的性能
110 | ### NTK-Aware Interpolation
111 | 高频信息对于神经网络非常重要,保留高频信息
112 | 高频分量旋转速度降幅低,低频分量旋转速度降幅高,即越靠后的分组旋转弧度缩小的倍数越大
113 | 在高频部分进行外推,低频部分进行内插。
114 | ### NTK-by-parts Interpolation
115 | 基于NTK-Aware Interpolation进行优化,不改变高频部分,仅缩小低频部分的旋转弧度。也就是不改变靠前分组的旋转弧度,仅减小靠后分组的旋转弧度
116 | ### Dynamic NTK Interpolation
117 | 当超出训练长度时,上述插值方法都比原模型直接外推的效果更好,但是它们都有一个共同的缺点,在训练长度内,推理表现都比原模型差
118 |
119 | 推理长度小于等于训练长度时,不进行插值
120 |
121 | 推理长度大于训练长度时,每一步都通过NTK-Aware Interpolation动态放大base,每一次生成都会重新调整旋转弧度,然后再进行下一次生成
122 |
123 | ## 多模态
124 | https://github.com/wdndev/mllm_interview_note/blob/main/02.mllm%E8%AE%BA%E6%96%87/0.%E4%BB%8E%E8%A7%86%E8%A7%89%E8%A1%A8%E5%BE%81%E5%88%B0%E5%A4%9A%E6%A8%A1%E6%80%81%E5%A4%A7%E6%A8%A1%E5%9E%8B.md
125 |
126 | ## LLM幻觉
127 | https://www.zhihu.com/people/swtheking/posts
128 | ### 分类
129 | 1.1 事实性问题(Factuality)
130 | 模型回答与事实不一致或在真实世界无法考证
131 |
132 | 1.2 忠诚度问题(Faithfulness)
133 | 模型回答没有遵从指令或者模型回答和上下文内容存在不一致
134 |
135 | 1.3 自我矛盾(self-Contradiction)
136 | 模型回答内部问题存在逻辑矛盾,比如COT多步推理之间存在矛盾
137 |
138 | ### 来源
139 | 数据源:错误训练数据,重复偏差,社会偏见,领域知识匮乏,知识过时未更新
140 |
141 | 训练:训练时teacher-force策略和推理策略的不一致性;指令微调样本的知识部分超出预训练知识的范畴,导致微调过程错误引导模型回答本身压缩知识范围之外的问题,从而加重了模型幻觉
142 |
143 | 推理:注意力机制的长程衰减;解码过程的错误累计
144 |
145 | ### 解决办法
146 | 高质量低事实错误的预训练数据集构建,通过模型、规则筛选高质量web数据源
147 |
148 | 降低重复偏见:使用SimHash、SemDeDup等消重技术对预训练数据进行消重
149 |
150 | 降低社会偏见
151 |
152 | 知识编辑
153 |
154 | RAG
155 |
156 | 反思,后处理:利用模型自我修正能力,先让模型生成答案,再使用prompt让模型对答案进行多角度的校验提问,并回答这些提问,最后基于以上回答修正初始答案
157 |
158 | ## 百面LLM中比较好的问题
159 | https://www.zhihu.com/people/swtheking/posts
160 |
161 | ### ROPE是低频部分保持远程衰减,还是高频部分保持远程衰减?
162 | 低频
163 |
164 | ### 为什么现在都是decoder-only架构
165 | 众所周知,Attention矩阵一般是由一个低秩分解的矩阵加softmax而来,具体来说是一个n×d的矩阵与d×n 的矩阵相乘后再加softmax(n≫d),这种形式的Attention的矩阵因为低秩问题而带来表达能力的下降。而Decoder-only架构的Attention矩阵是一个下三角阵,注意三角阵的行列式等于它对角线元素之积,由于softmax的存在,对角线必然都是正数,所以它的行列式必然是正数,即Decoder-only架构的Attention矩阵一定是满秩的!满秩意味着理论上有更强的表达能力,也就是说,Decoder-only架构的Attention矩阵在理论上具有更强的表达能力,改为双向注意力反而会变得不足。
166 |
167 | decoder-only的预训练目标和下游任务一致。
168 |
169 | 下三角或上三角mask更能够把位置编码的信息处理得更好?带来了位置识别上的优势,它打破了transformer的置换不变性,直接引入了从左往右的序,所以甚至不加位置编码都行
170 |
171 | ### 为什么需要RLHF?SFT不够吗?
172 | 数据更好搞
173 |
174 | 除了正确答案之外还要知道错误答案
175 |
176 | RLHF中的数据更多的包含模型输出的安全性、伦理性、政治以及用户的指令遵循
177 |
178 | RLHF的泛化性能比SFT好,但多样性会有所降低。
179 |
180 | ### DPO是on-policy还是off-policy
181 | DPO是一个off-policy的算法,因为训练DPO的pair数据不一定来自ref policy或者sft policy。优势是不需要对模型进行采样,然后标注,直接可以拿已有的数据集进行训练,这样的情况下包括采样的成本和标注的成本都可以节约。劣势是效果很难保证,尤其是你的模型本身能力和发布的pair数据不匹配的时候
182 |
183 | ### PPO是on-policy还是off-policy
184 | 近似on-policy
185 |
186 | 根据off-policy的定义,采样的网络和要优化的网络不是一个网络,那么对于PPO来说,使用一批数据从更新actor的第二个epoch开始,数据虽然都是旧的actor采样得到的,但是我们并没有直接使用这批数据去更新我们的新的actor,而是使用imporance sampling先将数据分布不同导致的误差进行了修正。那么这个importance sampling的目的就是让这两者数据分布之间的差异尽可能的缩小,那么就可以近似理解成做了importance sampling之后的数据就是我们的更新(这里的更新指的是多个epoch更新的中间过程)后的actor采样得来的,这样就可以理解成我们要优化得actor和采样得actor是同一个actor,那么他就是on-policy的。
187 |
188 | ### 可以跳过sft阶段直接进行rlhf吗
189 | 现阶段来看是不太可能的。模型如果纯进行RL的话,搜索空间过于庞大,消耗资源较多,利用sft首先做模仿学习缩小搜索空间,再利用RLHF进行进一步对齐是必要的。
190 | 参见DeepSeek的R1-Zero
191 |
192 | ### 同等MOE模型的loss能下降到和同等规模Dense模型的水准吗?
193 | 不能,因为MOE在训练中每个token forward和backward的实际的激活参数是远少于同等规模的Dense 模型的(Btw,尽管Dense模型训练完也是个偏向sparse的模型,也就是有少量神经元被激活,但是在训练中,Dense模型是可以自由选择激活哪部分神经元的。而Sparse Moe,通过训练路由来控制哪个token激活哪部分的expert,本质差距还蛮远的)那么从DeepseekV2-MOE-236B来看,激活21B,总参 236B,等效一个 90B 的Dense,从Deepseek-Coder-MOE-16B,激活2.4B,总参数16B,等效于一个7B模型。(等效计算是和激活参数,总参数都挂钩的函数计算出来的。)
194 |
195 | ### RLHF的performance上界是什么
196 | RLHF的performance上界就是rm模型的泛化上界
197 |
198 | ## 很细节的面试题
199 | ### dpo里面有reward model吗
200 | 无
201 |
202 | ### dpo在一开始训练时的loss是多少
203 | 看一下dpo公式就知道,由于一开始actor和reference是一样的,可以推算出loss是-log(sigmoid(0))
204 |
205 | ### GRPO是on-policy还是off-policy
206 | 同PPO,on-policy
207 |
208 | ### GRPO存在的问题
209 | + 熵崩塌:策略(Policy)的熵值(Entropy)急剧下降,导致策略的随机性显著降低,智能体过早放弃探索,陷入局部最优的现象
210 | + 奖励噪声:环境或奖励函数中存在的不确定性或干扰信号,导致智能体收到的奖励R(s,a),与实际动作价值偏离。噪声可能来自环境本身的不确定性,或人为设计的不合理奖励函数
211 | + 训练不稳定性:策略或价值函数的更新出现剧烈波动,导致训练曲线震荡、收敛缓慢甚至发散的现象
212 |
213 | ### deepseek V3中的MOE负载均衡是训练多久调整一次
214 | 每个训练step
215 |
216 | ## 除了力扣以外,面试可能会考察的Code
217 | ### 多头注意力实现
218 | ```python
219 | from math import sqrt
220 | import torch
221 | import torch.nn as nn
222 |
223 | class MultiHeadSelfAttention(nn.Module):
224 | dim_in: int # input dimension
225 | dim_k: int # key and query dimension
226 | dim_v: int # value dimension
227 | num_heads: int # number of heads, for each head, dim_* = dim_* // num_heads
228 |
229 | def __init__(self, dim_in, dim_k, dim_v, num_heads=8):
230 | super(MultiHeadSelfAttention, self).__init__()
231 | assert dim_k % num_heads == 0 and dim_v % num_heads == 0, "dim_k and dim_v must be multiple of num_heads"
232 | self.dim_in = dim_in
233 | self.dim_k = dim_k
234 | self.dim_v = dim_v
235 | self.num_heads = num_heads
236 | self.linear_q = nn.Linear(dim_in, dim_k, bias=False)
237 | self.linear_k = nn.Linear(dim_in, dim_k, bias=False)
238 | self.linear_v = nn.Linear(dim_in, dim_v, bias=False)
239 | self._norm_fact = 1 / sqrt(dim_k // num_heads)
240 |
241 | def forward(self, x):
242 | # x: tensor of shape (batch, n, dim_in)
243 | batch, n, dim_in = x.shape
244 |
245 | nh = self.num_heads
246 | dk = self.dim_k // nh # dim_k of each head
247 | dv = self.dim_v // nh # dim_v of each head
248 |
249 | q = self.linear_q(x).reshape(batch, n, nh, dk).transpose(1, 2) # (batch, nh, n, dk)
250 | k = self.linear_k(x).reshape(batch, n, nh, dk).transpose(1, 2) # (batch, nh, n, dk)
251 | v = self.linear_v(x).reshape(batch, n, nh, dv).transpose(1, 2) # (batch, nh, n, dv)
252 |
253 | dist = torch.matmul(q, k.transpose(2, 3)) * self._norm_fact # batch, nh, n, n
254 |
255 | mask = torch.triu(torch.ones(n, n), diagonal=0).bool()
256 | dist = dist.masked_fill(mask, -float("inf"))
257 |
258 | dist = torch.softmax(dist, dim=-1) # batch, nh, n, n
259 |
260 | att = torch.matmul(dist, v) # batch, nh, n, dv
261 | att = att.transpose(1, 2).reshape(batch, n, self.dim_v) # batch, n, dim_v
262 | return att
263 | ```
264 |
265 | ### Beam Search简易实现
266 | ```python
267 | def beam_search_decoder(decoder, k, max_time_steps):
268 | sequences = [[[''], 1.0]]
269 | re = []
270 | while k and max_time_steps:
271 | all_candidates = list()
272 | for i in range(len(sequences)):
273 | seq, score = sequences[i]
274 | token_list, scores = decoder(seq)
275 | for token, s in zip(token_list, scores):
276 | candidate = [seq + [token], score * s]
277 | all_candidates.append(candidate)
278 | ordered = sorted(all_candidates, key=lambda tup: tup[1]) # 按score排序
279 | sequences = ordered[-k:] # 选择前k个最好的
280 | tmp = []
281 | for seq in sequences:
282 | if seq[0][-1] == '':
283 | re.append(seq)
284 | k -= 1
285 | else:
286 | tmp.append(seq)
287 | sequences = tmp
288 | max_time_steps -= 1
289 | return sequences
290 | ```
291 |
292 | ### Transformer Position Embedding
293 | ```python
294 | def get_positional_encoding(max_seq_len, embed_dim):
295 | # embed_dim: 字嵌入的维度
296 | # max_seq_len: 最大的序列长度
297 | positional_encoding = np.array([
298 | [pos / np.power(10000, 2 * i / embed_dim) for i in range(embed_dim)]
299 | for pos in range(max_seq_len)])
300 |
301 | positional_encoding[1:, 0::2] = np.sin(positional_encoding[1:, 0::2]) # dim 2i 偶数
302 | positional_encoding[1:, 1::2] = np.cos(positional_encoding[1:, 1::2]) # dim 2i+1 奇数
303 | return positional_encoding
304 | ```
305 |
306 | ### MLM
307 | ```python
308 | for index in mask_indices:
309 | #80% of the time, replace with [MASK]
310 | if random.random() < 0.8:
311 | masked_token = "[MASK]"
312 | else:
313 | # 10% of the time, keep original
314 | if random.random() < 0.5:
315 | masked_token = tokens[index]
316 | # 10% of the time, replace with random word
317 | else:
318 | masked_token = random.choice(vacab_list)
319 | ```
320 |
321 | ### numpy实现attention
322 | ```python
323 | import numpy as np
324 |
325 | def masked_attention(query, key, value, mask=None):
326 | # 计算点积
327 | energy = np.matmul(query, key.T)
328 |
329 | # 如果提供了mask,则应用mask
330 | if mask is not None:
331 | energy[:, ~mask] = -np.inf # 将mask外的点积结果设置为负无穷
332 |
333 | # 计算注意力权重
334 | attention = np.softmax(energy, axis=1)
335 |
336 | # 将注意力权重应用于值
337 | return np.matmul(attention, value)
338 |
339 | # 示例:
340 | query = np.random.rand(3, 10)
341 | key = np.random.rand(3, 10)
342 | value = np.random.rand(3, 10)
343 | mask = np.array([True, False, True])
344 |
345 | # 应用带mask的注意力
346 | result = masked_attention(query, key, value, mask=mask)
347 |
348 | print(result.shape) # 输出结果的形状
349 | ```
350 |
351 | ### numpy实现梯度反向传播
352 | ```python
353 | import numpy as np
354 |
355 | # 生成模拟数据
356 | np.random.seed(42)
357 | true_k = 2.5
358 | true_b = 1.0
359 | x = np.linspace(0, 1, 100)
360 | y = true_k * x + true_b + np.random.normal(0, 0.1, size=x.shape)
361 |
362 | # 初始化参数
363 | k = np.random.randn()
364 | b = np.random.randn()
365 |
366 | learning_rate = 0.1
367 | epochs = 1000
368 | for epoch in range(epochs):
369 | y_pred = k * x + b
370 | loss = np.mean((y_pred - y)**2)
371 | # 对k的梯度: dL/dk = 2 * (y_pred - y) * x
372 | grad_k = 2 * np.mean((y_pred - y) * x)
373 | # 对b的梯度: dL/db = 2 * (y_pred - y)
374 | grad_b = 2 * np.mean(y_pred - y)
375 | k -= learning_rate * grad_k
376 | b -= learning_rate * grad_b
377 | print(f'\nFinal parameters: k = {k:.4f}, b = {b:.4f}')
378 | ```
379 |
380 | ### ELO评分
381 | ```python
382 | from collections import defaultdict
383 | import pandas as pd
384 |
385 | def compute_online_elo(battles, K=4, SCALE=400, BASE=10, INIT_RATING=1000):
386 | # Default rating initialization for each model
387 | rating = defaultdict(lambda: INIT_RATING)
388 |
389 | # Iterate through the dataframe to update the Elo ratings
390 | for rd, model_a, model_b, winner in battles[['model_a', 'model_b', 'winner']].itertuples():
391 | ra = rating[model_a]
392 | rb = rating[model_b]
393 |
394 | # Calculate expected outcomes for both models
395 | ea = 1 / (1 + BASE ** ((rb - ra) / SCALE))
396 | eb = 1 / (1 + BASE ** ((ra - rb) / SCALE))
397 |
398 | # Determine the actual score for model_a based on the winner
399 | if winner == "model_a":
400 | sa = 1 # model_a wins
401 | elif winner == "model_b":
402 | sa = 0 # model_b wins
403 | elif winner == "tie":
404 | sa = 0.5 # Tie
405 | else:
406 | raise Exception(f"unexpected vote {winner}")
407 |
408 | # Update ratings based on the outcome
409 | rating[model_a] += K * (sa - ea)
410 | rating[model_b] += K * ((1 - sa) - eb)
411 |
412 | return rating
413 | ```
414 |
--------------------------------------------------------------------------------