认认真真准备算法面试题-基础题

认认真真准备算法面试题-基础题

这里说到认认真真就是不单单背诵八股文而是真正理解含义,从数学上从原理上。

一 关于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矩阵初始值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值