这里写目录标题
LLM输入时的理解 1. Tokenizer的实现:Word极大似然估计 LLM推理:关于Attention mask的理解 1. CausalModel 与 AttentionMask 2. attention mask乘法变成加法 3. 参考代码 4. KV cache LLM推理:生成的策略有哪些? 1. 生成下一个token时候的sample策略 2. input length 以及超出长度后如何处理 MoE: DeepSeek-V2是怎么实现的? lora的Perf代码实现与使用 模型量化bitsandbytes代码实现使用 deepspeed模型切片分卡代码实现与使用 最新研究进展了解:LLM输入时的理解
1. Tokenizer的实现:Word极大似然估计
LLM推理:关于Attention mask的理解
1. CausalModel 与 AttentionMask
先看一个简易的attention的实现
import torch
# 随机生成Q,K,V仿真输入
batch, max_length, dim = 2, 4, 3
Q, K, V = torch.rand(3, batch, max_length, dim)
# LLM处于prefilling阶段,输入的token数为3个要推理第四个了
cur_length = 3 # prefilling stage cur_length < max_length
# 只需要前3个token参与运算即可
W = torch.einsum('bmd,bnd->bmn', Q[:, :cur_length], K[:, :cur_length])
Y = torch.einsum('bmn,bnd->bmd', W, V[:, :cur_length])
添加attention_mask
# 构建attention_mask,由于使用了cur_length作为切片,所以只要构建一个全是1的mask即可
atten_mask = torch.ones(max_length)
cur_length = 3 # prefilling stage cur_length < max_length
atten_mask = atten_mask[:cur_length].repeat(cur_length, 1)
print("attention mask:\n", atten_mask)
# 只需要前3个token参与运算即可
W = torch.einsum('bmd,bnd->bmn', Q[:, :cur_length], K[:, :cur_length])
W = torch.softmax((W * atten_mask[None, :, :]), dim=-1)
Y = torch.einsum('bmn,bnd->bmd', W, V[:, :cur_length])
当前的LLM模型往往采用CausalModel,它的mask构建如下,即计算Y[i]时不会有Q[i+t]/K[i+t]/V[i+t] (t>0)引入attention,Q[i]不会与K[i+t]/V[i+t]计算
idx = torch.arange(cur_length)
causal_mask = (idx[None, :] <= idx[:, None]).float()
print("causal_mask:\n", causal_mask)
输出如下:
attention mask:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
causal_mask:
tensor([[1., 0., 0.],
[1., 1., 0.],
[1., 1., 1.]])
2. attention mask乘法变成加法
原理如下:
exp(x * 0) = exp(x + (-inf)) ~= exp(x + min)
exp(x * 1) = exp(x + 0)
因此下面两个实现是等价的:
idx = torch.arange(cur_length)
causal_mask = (idx[None, :] <= idx[:, None]).float()
print("causal_mask:\n", causal_mask)
W = torch.einsum('bmd,bnd->bmn', Q[:, :cur_length], K[:, :cur_length])
W = torch.softmax((W * causal_mask[None, :, :]), dim=-1)
Y = torch.einsum('bmn,bnd->bmd', W, V[:, :cur_length])
print(Y.shape)
print(Y)
neg_inf = torch.finfo(Q.dtype).min
m = torch.zeros_like(causal_mask)
m[causal_mask == 0] = neg_inf
causal_mask = m
W = torch.einsum('bmd,bnd->bmn', Q[:, :cur_length], K[:, :cur_length])
W = torch.softmax((W + causal_mask[None, :, :]), dim=-1)
Y = torch.einsum('bmn,bnd->bmd', W, V[:, :cur_length])
print(Y.shape)
print(Y)
输出如下:
causal_mask:
tensor([[1., 0., 0.],
[1., 1., 0.],
[1., 1., 1.]])
torch.Size([2, 3, 3])
tensor([[[0.4954, 0.8203, 0.4379],
[0.5537, 0.8697, 0.4225],
[0.4435, 0.7512, 0.4051]],
[[0.4347, 0.9123, 0.5548],
[0.4604, 0.9270, 0.6272],
[0.4866, 0.9341, 0.6768]]])
torch.Size([2, 3, 3])
tensor([[[0.5419, 0.9893, 0.6665],
[0.6266, 0.9406, 0.4199],
[0.4435, 0.7512, 0.4051]],
[[0.2483, 0.8655, 0.2131],
[0.4129, 0.9380, 0.6106],
[0.4866, 0.9341, 0.6768]]])
3. 参考代码
causal_mask有两处实现,一个是huggingface的transformers中的基本类PreTrainedModel,它的一个负类ModuleUtilsMixin中,这个代码只有在config中is_decoder为True时才被使用,而往往这个是False(默认也是)
class ModuleUtilsMixin:
@staticmethod
def create_extended_attention_mask_for_decoder(input_shape, attention_mask, device=None):
if device is not None:
warnings.warn(
"The `device` argument is deprecated and will be removed in v5 of Transformers.", FutureWarning
)
else:
device = attention_mask.device
batch_size, seq_length = input_shape
seq_ids = torch.arange(seq_length, device=device)
causal_mask = seq_ids[None, None, :].repeat(batch_size, seq_length, 1) <= seq_ids[None, :, None]
# in case past_key_values are used we need to add a prefix ones mask to the causal mask
# causal and attention masks must have same type with pytorch version < 1.3
causal_mask = causal_mask.to(attention_mask.dtype)
if causal_mask.shape[1] < attention_mask.shape[1]:
prefix_seq_len = attention_mask.shape[1] - causal_mask.shape[1]
causal_mask = torch.cat(
[
torch.ones((batch_size, seq_length, prefix_seq_len), device=device, dtype=causal_mask.dtype),
causal_mask,
],
axis=-1,
)
extended_attention_mask = causal_mask[:, None, :, :] * attention_mask[:, None, None, :]
return extended_attention_mask
if self.config.is_decoder:
extended_attention_mask = ModuleUtilsMixin.create_extended_attention_mask_for_decoder(
input_shape, attention_mask, device
)
因此,很多代码是自己实现这个mask,以InterLM为例子
causal_mask *= torch.arange(target_length, device=device) > cache_position.reshape(-1, 1)
causal_mask = causal_mask[None, None, :, :].expand(input_tensor.shape[0], 1, -1, -1)
4. KV cache
KV cache原理
T=1
T=2
T=3
X1_1
X2_1
X2_2
X1_2
X2_3
X1_3
X3_1
X3_2
X3_3
通过上面的分析,可以知道,对于CausalModel的LLM,第t个词的结果在整过过程是不变的,且不依赖于后面时刻的输入,所以可以使用KV cache,把之前的结果缓存下来,只预测新的token的结果。
为什么需要KV cache | KV cache工作机制 | 有几个阶段 |估计KV cache 显存占用 | KV cache为何成为长上下文瓶颈 |优化方案 | 最新技术
sumary:
完全避免KV cache问题,可能依赖于架构的调整,比如换成 Mamba (Falcon Mamba 7B)
如何加速量化模型推理:将乘法计算转换为LUT查表
LLM推理:生成的策略有哪些?
1. 生成下一个token时候的sample策略
严昕:解读transformers库generate接口的解码策略
2. input length 以及超出长度后如何处理
MoE: DeepSeek-V2是怎么实现的?
用PyTorch从零开始编写DeepSeek-V2
lora的Perf代码实现与使用
模型量化bitsandbytes代码实现使用
deepspeed模型切片分卡代码实现与使用
最新研究进展了解:
视觉-语言模型(Vision-Language Model)方面有哪些值得关注的学者?
总结
### 文章总结本文围绕大型语言模型(LLM)的多个关键方面进行了深入探讨,包括输入理解、推理机制、生成策略、模型优化以及最新研究进展。以下是文章的核心要点总结:
#### 1. LLM输入时的理解
- **Tokenizer实现**:使用Word极大似然估计进行分词处理。
- **Attention Mask**:
- 在LLM推理中,引入了Attention Mask来控制注意力权重的计算,尤其在Causal Model中,确保每个token在生成时不考虑未来的token。
- 展示了如何构建attention mask及其乘法和加法的等价变换。
- 参考了Hugging Face Transformers和自实现如InterLM的具体实现方式。
#### 2. KV Cache
- **作用与工作机制**:为了减少重复计算和提高效率,LLM采用KV Cache存储历史token的K/V向量,使得新生成的token只需计算新的Q与已有K/V之间的Attention。
- **性能优化与资源占用**:讨论了KV Cache在显存占用上的挑战及其优化方案(如量化技术),并介绍了最新技术如YOCO在层间KV Cache的应用。
#### 3. LLM推理的生成策略
- **Sample策略**:解读了不同sample策略在生成下一个token时的应用,如transformers库中的generate接口策略。
- **Input Length处理**:讨论了如何处理输入长度超出限制的情况。
#### 4. MoE: DeepSeek-V2的实现
- 介绍了如何从零开始使用PyTorch编写DeepSeek-V2模型,探索混合专家系统(MoE)在LLM中的应用。
#### 5. 模型优化技术
- **Lora & Perf**:讨论了Lora (Low-Rank Adaptation) 的Perf实现与使用,进行轻量级模型调整。
- **模型量化**:使用Bits&Bytes框架对模型进行量化,以减少模型大小和推理时间。
- **Deepspeed切片分卡**:介绍了如何使用Deepspeed进行模型切片和并行化部署,以提升训练与推理效率。
#### 6. 最新研究进展
- **视觉-语言模型**:提及了视觉-语言模型方向的代表性学者,展示了该领域的最新动态。
综上所述,本文全面覆盖了LLM在理解输入、实现推理、优化生成策略以及最新技术进展等方面的多个重要面容,为研究者与实践者提供了宝贵的参考。