认认真真准备算法面试题-基础题
这里说到认认真真就是不单单背诵八股文而是真正理解含义,从数学上从原理上。
一 关于transformer结构
1 介绍self-attention
2 decoder为什么使用layernorm而不使用batchnorm?再追问为什么使用Norm呢?
1 Layer Normalization定义概念:
Layer Normalization是在特征维度上进行归一化的。它计算每个样本的均值和标准差,对每个样本的所有特征进行归一化,不依赖于其他样本的值。
这使得Layer Normalization适用于小批量以及变长序列数据,常用于RNN和Transformer中。
2 Batch Normalization定义概念:
Batch Normalization是在批量维度上进行归一化。它会计算整个批次的均值和标准差,然后对该批次的每个样本进行归一化。
这依赖于批量数据,因此在批量小或者序列数据(如RNN)中可能表现不佳。
5 norm和不加norm的训练收敛对比
6 代码
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# 生成模拟数据
def generate_data(n_samples=1000):
x = torch.randn(n_samples, 10) # 10个输入特征
y = (x.sum(dim=1) > 0).float() # 基于特征之和生成标签
return x, y
# 定义没有归一化的模型
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc1 = nn.Linear(10, 20)
self.fc2 = nn.Linear(20, 1)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
# 定义使用层归一化的模型
class SimpleModelWithLayerNorm(nn.Module):
def __init__(self):
super(SimpleModelWithLayerNorm, self).__init__()
self.fc1 = nn.Linear(10, 20)
self.ln = nn.LayerNorm(20)
self.fc2 = nn.Linear(20, 1)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.ln(x) # 使用层归一化
x = self.fc2(x)
return x
# 训练模型并记录损失
def train_model(model, x, y, epochs=100):
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
losses = []
for epoch in range(epochs):
optimizer.zero_grad()
outputs = model(x)
loss = criterion(outputs.squeeze(), y)
loss.backward()
optimizer.step()
losses.append(loss.item())
return losses
# 主程序
x, y = generate_data()
# 训练没有归一化的模型
model_no_norm = SimpleModel()
losses_no_norm = train_model(model_no_norm, x, y)
# 训练使用层归一化的模型
model_with_ln = SimpleModelWithLayerNorm()
losses_with_ln = train_model(model_with_ln, x, y)
# 可视化损失变化
plt.figure(figsize=(10, 5))
plt.plot(losses_no_norm, label='No Normalization')
plt.plot(losses_with_ln, label='With Layer Normalization')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Training Loss Comparison')
plt.legend()
plt.grid()
plt.show()
3 stablediffuse里面的cross
attention作用?在decoder中的作用
4 KVcache 为什么 不保存Q 😄
https://blog.csdn.net/stephen147/article/details/140956947
参考连接:https://zhuanlan.zhihu.com/p/708120479?utm_id=0
证明等价
1 从矩阵乘法角度QKT结果为Q2K1;Q2K2,
-> 与后面V矩阵乘法 -> 得到ATT2 = Q2K1V1 + Q2K2V2 -> ATTcur 只与Qcur以及 KV 缓存和KVcur相关,所以Q不用缓存,KV均参与计算所以都需要缓存。
思路为 Q1Q2正常输入时 最后att2的值 与 带有cache的Q2输入时求得att2的值是否相等 => 矩阵乘法的性质 可得相等 => Q1Q2输入与Q2输入效果一样,切保存Q1的目的就在Q2计算时使用但是在矩阵乘法中发现可以不用使用所以没有必要保存Q。
2 从更细节的softmax推理
其实这里就算没有mask参与,也能证明Attcur只与当前Qcur相关 以及KVcur。
值得注意是softmax计算矩阵时的变换。
总结就是Q时动态特性,KV时静态特性
=> 那么 Q fisrt Tokens的信息去哪里? -> 实际是计算在Att1里面
4.1 引申mask在哪里作用的问题?
在训练和推理时候的mask作用:
自能看到自己和前面的,计算相似度。
矩阵乘法计算后再mask 计算遮挡。可能会浪费一些计算。
3)mask是根据前面shape动态产生的。
5 举一个算子改写的例子?
1)mask 上三角和原矩阵相乘
2)rope -> sin/cos
6 统计性能
NPU 计算 与 CPU 下发耗时
就是DDR搬运SRAM时间
在flashattention -> 下发耗时 -> 融合算子
7 kvcache应用新框架不用二次编码
NPU搭载vllm
东方问题
8 熟悉算力TOPS和带宽
9 介绍layer结构
单层
10 GQA介绍
11 如何评价量化指标 用到什么策略
12 如何多卡训练
13 对标
英伟达和升腾
14 熟悉后处理中的temperature 和 topk topp以及greedsearch 😄
14.1 temperature 😄
a.概念
temperature 参数通常用于控制生成文本的随机性或 创造性。基本原理为:影响模型输出的概率分布,从而影响生成结果的多样性。
b. 公式
先进行logit 然后再 做softmax。
从代码实验表现出来采样。
import torch
import torch.nn.functional as F
# 模拟模型生成的 logits
logits = torch.tensor([2.0, 1.0, 0.1])
def sample_with_temperature(logits, temperature):
# 应用温度调整
adjusted_logits = logits / temperature
# 计算经过 softmax 转换后的概率
probabilities = F.softmax(adjusted_logits, dim=0)
# 从概率中采样一个索引
sampled_index = torch.multinomial(probabilities, num_samples=1).item()
return sampled_index, probabilities
# 测试不同的温度值
temperature_values = [0.1, 1.0, 2.0]
for temp in temperature_values:
index, probs = sample_with_temperature(logits, temp)
print(f"Temperature: {temp}, Sampled Index: {index}, Probabilities: {probs.tolist()}")
三种情况采样概率对比。
Temperature: 0.1, Sampled Index: 0, Probabilities: [0.9999545812606812, 4.539786823443137e-05, 5.6025419858940495e-09]
Temperature: 1.0, Sampled Index: 1, Probabilities: [0.6590011715888977, 0.24243298172950745, 0.09856589138507843]
Temperature: 2.0, Sampled Index: 1, Probabilities: [0.50168776512146, 0.3042890131473541, 0.1940232515335083]
当temperature 取值为2时候,index=2的 概率已经从原来的5e-9 提升到0.19, index=1的概率从4e-5提升到了0.3, 这样就完全可以与 index=0的0.5,对决采样概率了。
0 区域确定,越大采样更多样。
14.2 topk 😄
a.概率
后处理过程中,temperature 和 top-k 采样参数的顺序和结合使用能够显著影响生成文本的多样性和质量。
top-k 是一种采样方法,它会从模型输出中只选择前 k 个概率最高的词汇,这个方法有效地过滤掉了概率较低的词,减少了生成文本中偏离主题或无关内容的风险。
b.顺序
模型会生成 logits
通过 top-k 筛选出概率最高的 k 个 logits。
对 top-k 的 logits 应用 temperature 调整。
分析:先进行 Top-k 筛选可以减少低概率项对结果的影响。
c 代码实验
import torch
import torch.nn.functional as F
# 模拟模型输出的 logits
logits = torch.tensor([2.0, 1.0, 0.5, 1.5, 0.1])
def top_k_logits(logits, k):
# 从 logits 中选择前 k 个最大的值
values, indices = torch.topk(logits, k)
result = torch.full(logits.shape, float('-inf')) # 将其他元素设为负无穷
result[indices] = values # 保留 top-k 的 logits
return result
def sample_with_temperature_and_top_k(logits, temperature, k):
# 应用 Top-k 筛选
filtered_logits = top_k_logits(logits, k)
# 应用 Temperature
adjusted_logits = filtered_logits / temperature
# 获取概率分布
probabilities = F.softmax(adjusted_logits, dim=0)
# 从概率中采样一个索引
sampled_index = torch.multinomial(probabilities, num_samples=1).item()
return sampled_index, probabilities
# 测试参数
temperature_values = [0.5, 1.0, 1.5]
k_value = 3
for temp in temperature_values:
index, probs = sample_with_temperature_and_top_k(logits, temp, k_value)
print(f"Temperature: {temp}, Sampled Index: {index}, Probabilities: {probs.tolist()}")
结果显示:
Temperature: 0.5, Sampled Index: 0, Probabilities: [0.6652408838272095, 0.09003056585788727, 0.0, 0.2447284460067749, 0.0]
Temperature: 1.0, Sampled Index: 0, Probabilities: [0.5064803957939148, 0.18632373213768005, 0.0, 0.30719590187072754, 0.0]
Temperature: 1.5, Sampled Index: 0, Probabilities: [0.4484408497810364, 0.23023721575737, 0.0, 0.32132190465927124, 0.0]
14.3 topp 😄
a. 概念
top-p 会根据设定的累计概率阈值(p)动态选择 token。这意味着它并不固定选择前 k 个 token,而是会根据 logits 的实际分布情况决定所需 token 的数量。这样,当 logits 中的某个 token 具有高度优势时,top-p 可以适度放宽包容性,从而避免对较高概率 token 的过度依赖。
b.动态和静态
Top-k 和 top-p 策略则结合了静态和动态的筛选过程,进一步优化生成质量。
c.原理
指定一个概率阈值 p(例如 0.9),表示我们只考虑那些累计概率达到 p 的 token。
将 logits 转换为概率分布,并按照概率从高到低进行排序。
从高概率的 token 开始,计算累计概率,直到累积概率达到所设定的阈值 p。
所有累积概率之和不超过 p 的 token 组成候选集。这意味着选定的 token 数量是动态的,而不是固定的 k 个。
代码对比效果
import torch
import torch.nn.functional as F
# 设置随机种子
torch.manual_seed(42)
# 模拟模型的 logits (5个 token 的得分)
logits = torch.tensor([2.0, 1.0, 0.5, 1.5, 0.1])
def top_k_sampling(logits, k):
top_k_indices = torch.topk(logits, k).indices
filtered_logits = torch.full(logits.shape, float('-inf'))
filtered_logits[top_k_indices] = logits[top_k_indices]
return filtered_logits
def top_p_sampling(logits, p):
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
idx_to_keep = cumulative_probs <= p
filtered_logits = torch.full(logits.shape, float('-inf'))
filtered_logits[sorted_indices[idx_to_keep]] = sorted_logits[idx_to_keep]
return filtered_logits
def sample_from_logits(logits, num_samples=1):
probabilities = F.softmax(logits, dim=0)
return torch.multinomial(probabilities, num_samples=num_samples)
# 实验参数
top_k_value = 3 # Top-k 策略中选择的token数量
top_p_value = 0.9 # Top-p 策略中累积概率的阈值
num_samples = 10 # 生成样本数量
print("Original logits:", logits.tolist())
print("\nTop-k Sampling Results:")
for _ in range(num_samples):
filtered_logits_k = top_k_sampling(logits, top_k_value)
sampled_index_k = sample_from_logits(filtered_logits_k)
print(sampled_index_k.tolist())
print("\nTop-p Sampling Results:")
for _ in range(num_samples):
filtered_logits_p = top_p_sampling(logits, top_p_value)
sampled_index_p = sample_from_logits(filtered_logits_p)
print(sampled_index_p.tolist())
如果需要控制输出的限制,可以偏向使用 top-k。
如果想要更自然和多样化的输出,则可能倾向于 top-p。
14.4 greed search 😄
优点:
简单高效: 只需寻找当前最优解,计算成本相对较低。
一致性: 输出的文本通常较为一致,易于预测。
缺点:
缺乏多样性: 始终选择最高概率的 token 可能导致生成内容缺乏创意,容易陷入局部最优解。
忽略全局信息: 可能错过在整体上下文中更具吸引力的选项,从而使生成的文本质量下降。
14.5 repetition-penalty
用于减少生成文本中重复单词或短语的出现。这一机制的核心思想是在模型对每个单词的生成概率进行调整,使得已经生成过的单词在后续生成过程中被选择的概率减少。
生成概率: 在某一时刻 t,模型生成下一个单词的概率是由softmax函数生成的,公式如下:
当模型生成的单词在前面的序列中已经出现过时,我们可以通过一个惩罚因子来调整概率。假设某个单词
w已经在序列中出现过
n次,我们对这个单词的概率进行如下调整:
其中,penalty是一个小于 1 的因子,通常设置为如 0.8 或 0.9。
代码:
import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer
# 准备模型和tokenizer
model_name = "openai-community/gpt2" # 或者其他语言模型,如 "gpt2-medium"
tokenizer = GPT2Tokenizer.from_pretrained(model_name)
model = GPT2LMHeadModel.from_pretrained(model_name)
model.eval()
def generate_text_with_repetition_penalty(input_text, max_length=50, repetition_penalty=1.5):
input_ids = tokenizer.encode(input_text, return_tensors='pt')
generated_ids = input_ids.clone()
# 生成文本
for _ in range(max_length):
outputs = model(generated_ids)
logits = outputs.logits[:, -1, :] # 取最后一个时刻的logits
# 获取当前已生成的单词
generated_token = generated_ids.squeeze().tolist()
# 生成当前已经生成的单词集合
already_generated = set(generated_token)
# 应用重复惩罚
for i in range(len(logits[0])): # 对所有词汇表中的word
if i in already_generated: # 如果这个词已生成过
logits[0, i] /= repetition_penalty # 降低概率
# 计算新的概率分布
probabilities = torch.softmax(logits, dim=-1)
next_token_id = torch.multinomial(probabilities, num_samples=1) # 采样下一个token
# 将下一个token添加到已生成的序列中
generated_ids = torch.cat((generated_ids, next_token_id.unsqueeze(0)), dim=1)
# 检查是否生成结束token (如[END])
if next_token_id.item() == tokenizer.eos_token_id:
break
generated_text = tokenizer.decode(generated_ids[0], skip_special_tokens=True)
return generated_text
# 测试生成文本
input_text = "Once upon a time"
generated_text = generate_text_with_repetition_penalty(input_text)
print("Generated Text with Repetition Penalty:")
print(generated_text)
主要就是这块
# 应用重复惩罚
for i in range(len(logits[0])): # 对所有词汇表中的word
if i in already_generated: # 如果这个词已生成过
logits[0, i] /= repetition_penalty # 降低概率
15 从数学上解释为什么要用到多头的自注意力机制,而不只是注意力机制
1 在单头自注意力机制中,输入序列中的每个元素(例如,单词或特征)都会生成一个查询(Query)、一个键(Key)和一个值(Value),它们的计算可以表示为:
注意力权重是通过以下步骤计算的:
a 计算相似度(通常是点积)并进行缩放:
b 生成的输出是对所有值的加权和,其中权重由输入之间的关系定义。
2 多头自注意力机制
在多头自注意力机制中,整个过程的关键是并行计算多个注意力头。每个头都可以有自己独立的查询、键和值的权重矩阵,具体可表示为:
3 意义及理由
a更丰富的特征表示:通过使用多个头,每个头可以学习到不同的特征和上下文信息。每个头可以专注于输入数据的不同方面,因此可以更丰富地捕捉数据中的信息。例如,一个头可能捕捉到局部的相似性,而另一个头可能捕捉到全局的趋势。
b多样性:由于每个注意力头独立学习,这意味着它们能够为模型提供来自不同特征空间的视角,这样就可以在输入特征之间建立更复杂的关系。单头注意力则可能容易陷入固定的特征组合,导致表达能力的局限。
c正则化效果:多头机制在某种程度上起到了正则化的效果,因为不同的头可从多种不同的子空间中学习,减轻了模型对于单一特征的依赖,从而降低了过拟合风险。
d 计算效率:在实际实现中,多头注意力可以并行计算,提升了训练和推理的速度。这是因为不同的头之间不相互依赖,可以同时处理,实现更高效的数据处理能力。
总结
多头自注意力机制通过并行计算多个注意力头,为模型提供了更强的表达能力和更丰富的信息捕获。它允许模型高度灵活地在不同的特征空间中学习,增强了处理复杂任务的能力.
16 后处理中search的方法
greed search
beam search
查视频确定search
17 LLM写单元测试-agent与沙盒
18 对于perchannel-pertensor的理解
19 attention多种变化的理解
1 paged attention
2flash attention
二 必备的技能
1 深入理解smoothquant
1 用代码实现权重和激活之间的运算关系
2 理解什么是perchannel
3 理解超参数
参考:https://zhuanlan.zhihu.com/p/681158646
2 smoothquant和kvcache能否一起使用
3 深度理解lora
0 主要作用的层
1 PEFT 参数
from peft import get_peft_model, LoRAConfig
# 配置 LoRA
lora_config = LoRAConfig(
rank=8,
alpha=16,
target_modules=["q_proj", "v_proj"], # 选择需要微调的模块
dropout=0.1
)
# 获取微调后的模型
model = get_peft_model(original_model, lora_config)
2 如何设定AB矩阵初始值