└── 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 | --------------------------------------------------------------------------------