当前位置:AIGC资讯 > AIGC > 正文

【大模型与AIGC】VLM基础知识汇总

这里写目录标题

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:

因果结构的LLM在attention计算时候存在重复计算,且计算时间随着token数O(N^2)复杂度增长 存储前面token结果的K/V,只完成新的token的Q到结果的计算,从而将负责度降为线性增长。 Prefill和decoding两个阶段,prefill将输入的prompt计算并为每个token存储K/V,生成第一个token (gemm矩阵);decoding就是完成第二个到最后一个token的生成,同时记录新token的K/V (gemv向量)。 KV cache显存:层数 × KV注意力头的数量 × 注意力头的维度 × (位宽/8) × 2(K/V),一般使用bfloat16/float16,也就是位宽为16。 Llama 3 8B:32 × 8 × 128 × 2 × 2 = 131,072B ~= 0.1M, (Llama 3 8B 有 32 个注意力头,不过由于 GQA 的存在,只有 8 个注意力头用于键和值)。 Llama 3 70B: 80 × 8 × 128 × 2 × 2 = 327,680B ~=0.3M 如果上下文大小是 8192,Llama 3 8B 131,072x 8192 ~= 1.G,Llama 3 80B ~=2.5G;如果上下文大小是 128k,Llama 3 8B 131,072x 128000~=15.6G,Llama 3 80B ~=39.1G 优化方案:量化,4bit甚至2bit,但量化过程会减慢解码过程,并且可能显著影响 LLM 的准确性。使用时需要调整量化超参来减轻影响。 最新发展:层内KV Cache 共享(MQA/GQA)发展为层间KV-cache(YOCO)

完全避免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在理解输入、实现推理、优化生成策略以及最新技术进展等方面的多个重要面容,为研究者与实践者提供了宝贵的参考。

更新时间 2024-10-04