llama网络结构及源码

news2024/11/9 6:21:42

模型初始化

首先模型初始化,确定模型属性 

class LLaMA(nn.Module):
    def __init__(self, config: LLaMAConfig) -> None:
        super().__init__()
        assert config.padded_vocab_size is not None
        self.config = config

        self.lm_head = nn.Linear(config.n_embd, config.padded_vocab_size, bias=False)
        self.transformer = nn.ModuleDict(
            dict(
                wte=nn.Embedding(config.padded_vocab_size, config.n_embd),
                h=nn.ModuleList(Block(config) for _ in range(config.n_layer)),
                ln_f=RMSNorm(config.n_embd),
            )
        )

        self.rope_cache: Optional[RoPECache] = None
        self.mask_cache: Optional[MaskCache] = None
        self.kv_caches: List[KVCache] = []

config

确定模型参数,config使用默认的LLaMAConfig类

class LLaMAConfig:
    block_size: int = 2048
    vocab_size: int = 32000
    padded_vocab_size: Optional[int] = None
    n_layer: int = 32
    n_head: int = 32
    n_embd: int = 4096

    def __post_init__(self):
        if self.padded_vocab_size is None:
            self.padded_vocab_size = find_multiple(self.vocab_size, 64)

    @classmethod
    def from_name(cls, name: str) -> Self:
        return cls(**llama_configs[name])


llama_configs = {
    "7B": dict(n_layer=32, n_head=32, n_embd=4096),
    "13B": dict(n_layer=40, n_head=40, n_embd=5120),
    "30B": dict(n_layer=60, n_head=52, n_embd=6656),
    "65B": dict(n_layer=80, n_head=64, n_embd=8192),
}

对于所有类型的网络,不变的超参数为:
block_size: 模型处理的最大文本块的大小为2048
vocab_size: 词汇表的大小,即模型能识别的词汇总数为32000

padded_vocab_size经过find_multiple函数确定,用于确保词汇表大小是指定数值(64)的倍数
vocab_size=32000/64=500,词汇表大小是指定数值(64)的倍数
padded_vocab_size32000

def find_multiple(n: int, k: int) -> int:
    if n % k == 0:
        return n
    return n + k - (n % k)

根据网络的参数量不同,模型层数、维度和自主意力头数不同:选择7B模型的情况下,
n_layer: 模型的层数。32
n_head: 自注意力机制中的头数。32
n_embd: 词嵌入的维度或隐藏层的维度。4096

lm_head

线性层输入维度为4096输出维度为32000,没有偏置

self.lm_head = nn.Linear(config.n_embd, config.padded_vocab_size, bias=False)

transformer

nn.ModuleDict():和python字典一样存在键值对,可根据key选取网络

包括有嵌入层wte,隐藏层h和归一化层ln_f

self.transformer = nn.ModuleDict(
            dict(
                wte=nn.Embedding(config.padded_vocab_size, config.n_embd),
                h=nn.ModuleList(Block(config) for _ in range(config.n_layer)),
                ln_f=RMSNorm(config.n_embd),
            )
        )

wte

嵌入层生成词向量,输入维度为填充后词典的大小padded_vocab_size(32000),输出维度为词嵌入维度n_embd(4096)

h

nn.ModuleList():可以通过迭代的方式创建网络,和list的用法一致
n_layer(32)层网络块Block
其中网络块Block,包括前后两层归一化层RMSNorm,一层自主意力层CausalSelfAttention和一层线性层MLP

class Block(nn.Module):
    def __init__(self, config: LLaMAConfig) -> None:
        super().__init__()
        self.rms_1 = RMSNorm(config.n_embd)
        self.attn = CausalSelfAttention(config)
        self.rms_2 = RMSNorm(config.n_embd)
        self.mlp = MLP(config)
rms_1/rms_2

RMSNorm  详解三种常用标准化 Batch Norm & Layer Norm & RMSNorm_layernorm rmsnorm-CSDN博客
Llama改进之——均方根层归一化RMSNorm-CSDN博客

BatchNorm是对一个 batch 单个特征的所有样本做归一化
LayerNorm是对单个样本的所有特征做归一化

class RMSNorm(nn.Module):
    """Root Mean Square Layer Normalization.

    Derived from https://github.com/bzhangGo/rmsnorm/blob/master/rmsnorm_torch.py. BSD 3-Clause License:
    https://github.com/bzhangGo/rmsnorm/blob/master/LICENSE.
    """

    def __init__(self, size: int, dim: int = -1, eps: float = 1e-5) -> None:
        super().__init__()
        self.scale = nn.Parameter(torch.ones(size))
        self.eps = eps
        self.dim = dim

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # NOTE: the original RMSNorm paper implementation is not equivalent
        # norm_x = x.norm(2, dim=self.dim, keepdim=True)
        # rms_x = norm_x * d_x ** (-1. / 2)
        # x_normed = x / (rms_x + self.eps)
        norm_x = torch.mean(x * x, dim=self.dim, keepdim=True)
        x_normed = x * torch.rsqrt(norm_x + self.eps)
        return self.scale * x_normed
attn

CausalSelfAttention
首先声明嵌入层/隐藏层可以被注意力头数整除
包括c_attn层、c_proj层
注意力头数n_head(32),隐藏层维度n_embd(4096) ,最大文本块的大小block_size(2048)

c_attn

Q,K,V对应的线性层,输入维度为隐藏层维度n_embd(4096),输出维度为3倍的隐藏层维度n_embd(4096)*3分别对应Q,K,V,没有偏置

c_proj

当前模块Block的输出映射,输入维度为隐藏层维度n_embd(4096),输出维度为隐藏层维度n_embd(4096),没有偏置

线性层mlp

hidden_dim(4*4096=16384) ,n_hidden(int(2 * hidden_dim / 3)=10922)
判断是否能被256整除,对n_hidden进行修正,(n + k - (n % k))结果为11008
两个全连接层输入维度为4096,输出维度为11008
映射层输入维度为11008,输出维度为4096

class MLP(nn.Module):
    def __init__(self, config: LLaMAConfig) -> None:
        super().__init__()
        hidden_dim = 4 * config.n_embd
        n_hidden = int(2 * hidden_dim / 3)
        n_hidden = find_multiple(n_hidden, 256)

        self.c_fc1 = nn.Linear(config.n_embd, n_hidden, bias=False)
        self.c_fc2 = nn.Linear(config.n_embd, n_hidden, bias=False)
        self.c_proj = nn.Linear(n_hidden, config.n_embd, bias=False)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = F.silu(self.c_fc1(x)) * self.c_fc2(x)
        x = self.c_proj(x)
        return x

ln_f

RMSNorm,归一化维度为n_embd(4096)

rope_cache

存储或缓存与RoPE(旋转位置编码)相关的数据。Optional[RoPECache] 表示它可以是 RoPECache 类型的对象,也可以是 None

self.rope_cache: Optional[RoPECache] = None

mask_cache

用于缓存与掩码相关的数据。Optional[MaskCache] 表示它可以是 MaskCache 类型的对象,也可以是 None

self.mask_cache: Optional[MaskCache] = None

kv_caches

 用于存储多个 KVCache 类型的缓存对象,初始化为空列表

self.kv_caches: List[KVCache] = []

tokenizer

tokenizer初始化 

class Tokenizer:
    """Tokenizer for LLaMA."""

    def __init__(self, model_path: Path) -> None:
        self.processor = SentencePieceProcessor(model_file=str(model_path))
        self.bos_id = self.processor.bos_id()
        self.eos_id = self.processor.eos_id()
        self.pad_id = self.processor.pad_id()

 tokennizer.encoder

tokenizer.encode(prompt, bos=True, eos=False, device=fabric.device)
    def encode(
        self,
        string: str,
        bos: bool = True,
        eos: bool = False,
        max_length: int = -1,
        pad: bool = False,
        device: Optional[torch.device] = None
    ) -> torch.Tensor:
        tokens = self.processor.encode(string)
        if bos:
            tokens = [self.bos_id] + tokens
        if eos:
            tokens = tokens + [self.eos_id]
        if max_length > 0:
            tokens = tokens[:max_length]
        if pad and len(tokens) < max_length:
            tokens += [self.pad_id] * (max_length - len(tokens))

        return torch.tensor(tokens, dtype=torch.int, device=device)

位置编码和mask

logits = model(x, max_seq_length, input_pos)

 输入参数包括输入文本对应词库的idx,最大序列长度,现在输入文本的位置

 def forward(
        self, idx: torch.Tensor, max_seq_length: Optional[int] = None, input_pos: Optional[torch.Tensor] = None
    ) -> Union[torch.Tensor, Tuple[torch.Tensor, List[KVCache]]]:
        B, T = idx.size()

        block_size = self.config.block_size
        if max_seq_length is None:
            max_seq_length = block_size
        assert T <= max_seq_length, f"Cannot forward sequence of length {T}, max seq length is only {max_seq_length}"
        assert max_seq_length <= block_size, f"Cannot attend to {max_seq_length}, block size is only {block_size}"
        assert T <= block_size, f"Cannot forward sequence of length {T}, block size is only {block_size}"

        if self.rope_cache is None:
            self.rope_cache = self.build_rope_cache(idx)
        if self.mask_cache is None:
            self.mask_cache = self.build_mask_cache(idx)

        if input_pos is not None:
            rope = self.rope_cache.index_select(0, input_pos)
            mask = self.mask_cache.index_select(2, input_pos)
            mask = mask[:, :, :, :max_seq_length]
        else:
            rope = self.rope_cache[:T]
            mask = self.mask_cache[:, :, :T, :T]

        # forward the model itself
        x = self.transformer.wte(idx)  # token embeddings of shape (b, t, n_embd)

        if input_pos is None:  # proxy for use_cache=False
            for block in self.transformer.h:
                x, _ = block(x, rope, mask, max_seq_length)
        else:
            if not self.kv_caches:
                head_size = self.config.n_embd // self.config.n_head
                cache_shape = (B, self.config.n_head, max_seq_length, head_size)
                self.kv_caches = [
                    (torch.zeros(cache_shape, device=x.device, dtype=x.dtype), torch.zeros(cache_shape, device=x.device, dtype=x.dtype))
                    for _ in range(self.config.n_layer)
                ]
            for i, block in enumerate(self.transformer.h):
                x, self.kv_caches[i] = block(x, rope, mask, max_seq_length, input_pos, self.kv_caches[i])

        x = self.transformer.ln_f(x)

        logits = self.lm_head(x)  # (b, t, vocab_size)

        return logits

确定最大文本长度

将输入文本长度,本次文本的最大文本长度,模型能够处理的最长文本块大小进行对比,确定本次的最大文本长度

建立rope_cache

建立位置缓存

def build_rope_cache(
    seq_len: int, n_elem: int, dtype: torch.dtype, device: torch.device, base: int = 10000
) -> RoPECache:
    """Enhanced Transformer with Rotary Position Embedding.

    Derived from: https://github.com/labmlai/annotated_deep_learning_paper_implementations/blob/master/labml_nn/
    transformers/rope/__init__.py. MIT License:
    https://github.com/labmlai/annotated_deep_learning_paper_implementations/blob/master/license.
    """
    # $\Theta = {\theta_i = 10000^{\frac{2(i-1)}{d}}, i \in [1, 2, ..., \frac{d}{2}]}$
    theta = 1.0 / (base ** (torch.arange(0, n_elem, 2, dtype=dtype, device=device) / n_elem))

    # Create position indexes `[0, 1, ..., seq_len - 1]`
    seq_idx = torch.arange(seq_len, dtype=dtype, device=device)

    # Calculate the product of position index and $\theta_i$
    idx_theta = torch.outer(seq_idx, theta).float()

    cache = torch.stack([torch.cos(idx_theta), torch.sin(idx_theta)], dim=-1)

    # this is to mimic the behaviour of complex32, else we will get different results
    if dtype in (torch.float16, torch.bfloat16, torch.int8):
        cache = cache.half()
    return cache

输入的参数:模型能够处理的最大文本块大小,embeddiing维度/注意力头数

步骤:
1.计算$\Theta = {\theta_i = 10000^{\frac{2(i-1)}{d}}, i \in [1, 2, ..., \frac{d}{2}]}$

2.计算$\theta_i$和最大文本块各个位置的乘积


3.计算sin(idx_theta)和cos(idx_theta)

建立mask_cache

    def build_mask_cache(self, idx: torch.Tensor) -> MaskCache:
        ones = torch.ones((self.config.block_size, self.config.block_size), device=idx.device, dtype=torch.bool)
        return torch.tril(ones).unsqueeze(0).unsqueeze(0)

步骤:
1.建立最大文本块大小*最大文本块大小的全1(True,布尔型)矩阵
2.取对角线,在增加0,1维度,mask的size为(1,1,本次的最大文本长度,本次的最大文本长度)

确定RoPE和mask

根据输入文本位置确定旋转位置编码和mask

 if input_pos is not None:
            rope = self.rope_cache.index_select(0, input_pos)
            mask = self.mask_cache.index_select(2, input_pos)
            mask = mask[:, :, :, :max_seq_length]
        else:
            rope = self.rope_cache[:T]
            mask = self.mask_cache[:, :, :T, :T]

根据输入文本位置确定旋转位置编码

根据输入文本位置和本次的最大文本长度确定mask,size为(1,1,输入文本长度,本次输入的最大文本长度)

模型前向传播

生成词嵌入

 x = self.transformer.wte(idx)

embedding层生成词嵌入

隐藏层计算

 if input_pos is None:  # proxy for use_cache=False
            for block in self.transformer.h:
                x, _ = block(x, rope, mask, max_seq_length)
        else:
            if not self.kv_caches:
                head_size = self.config.n_embd // self.config.n_head
                cache_shape = (B, self.config.n_head, max_seq_length, head_size)
                self.kv_caches = [
                    (torch.zeros(cache_shape, device=x.device, dtype=x.dtype), torch.zeros(cache_shape, device=x.device, dtype=x.dtype))
                    for _ in range(self.config.n_layer)
                ]
            for i, block in enumerate(self.transformer.h):
                x, self.kv_caches[i] = block(x, rope, mask, max_seq_length, input_pos, self.kv_caches[i])

 确定kv_cache

步骤:
1.确定注意力头的维度
embedding维度/注意力头数

2.k,v的维度为(batch_size,注意力头数,本次的最大文本长度,各注意力头的维度)
3.对n_layer(32)层自注意力层,生成全0的k,v

进入transformer的隐藏层

for i, block in enumerate(self.transformer.h):
    x, self.kv_caches[i] = block(x, rope, mask, max_seq_length, input_pos, self.kv_caches[i])

每一层循环的输入包括:
词嵌入,位置嵌入,mask,本次的最大文本长度,输入位置,本次循环对应的kv_cache

    def forward(
        self,
        x: torch.Tensor,
        rope: RoPECache,
        mask: MaskCache,
        max_seq_length: int,
        input_pos: Optional[torch.Tensor] = None,
        kv_cache: Optional[KVCache] = None,
    ) -> Tuple[torch.Tensor, Optional[KVCache]]:
        h, new_kv_cache = self.attn(self.rms_1(x), rope, mask, max_seq_length, input_pos, kv_cache)
        x = x + h
        x = x + self.mlp(self.rms_2(x))
        return x, new_kv_cache
attention模块计算

更新kv_cache,计算自注意力

 输入包括:
归一化层,位置编码,mask,本次的最大文本长度,输入位置,kv_cache

RMSNorm前向
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # NOTE: the original RMSNorm paper implementation is not equivalent
        # norm_x = x.norm(2, dim=self.dim, keepdim=True)
        # rms_x = norm_x * d_x ** (-1. / 2)
        # x_normed = x / (rms_x + self.eps)
        norm_x = torch.mean(x * x, dim=self.dim, keepdim=True)
        x_normed = x * torch.rsqrt(norm_x + self.eps)
        return self.scale * x_normed
计算q,k,v
    def forward(
        self,
        x: torch.Tensor,
        rope: RoPECache,
        mask: MaskCache,
        max_seq_length: int,
        input_pos: Optional[torch.Tensor] = None,
        kv_cache: Optional[KVCache] = None,
    ) -> Tuple[torch.Tensor, Optional[KVCache]]:
        B, T, C = x.size()  # batch size, sequence length, embedding dimensionality (n_embd)

        # calculate query, key, values for all heads in batch and move head forward to be the batch dim
        q, k, v = self.c_attn(x).split(self.n_embd, dim=2)

        head_size = C // self.n_head
        k = k.view(B, T, self.n_head, head_size)
        q = q.view(B, T, self.n_head, head_size)
        v = v.view(B, T, self.n_head, head_size)

        q = apply_rope(q, rope)
        k = apply_rope(k, rope)

        k = k.transpose(1, 2)  # (B, nh, T, hs)
        q = q.transpose(1, 2)  # (B, nh, T, hs)
        v = v.transpose(1, 2)  # (B, nh, T, hs)

        if kv_cache is not None:
            cache_k, cache_v = kv_cache
            # check if reached token limit
            if input_pos[-1] >= max_seq_length:
                input_pos = torch.tensor(max_seq_length - 1, device=input_pos.device)
                # shift 1 position to the left
                cache_k = torch.roll(cache_k, -1, dims=2)
                cache_v = torch.roll(cache_v, -1, dims=2)
            k = cache_k.index_copy(2, input_pos, k)
            v = cache_v.index_copy(2, input_pos, v)
            kv_cache = k, v

        # causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)
        #  att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
        #  att = att.masked_fill(mask[:,:,:T,:T] == 0, float('-inf'))
        #  att = F.softmax(att, dim=-1)
        #  y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)

        # efficient attention using Flash Attention CUDA kernels
        y = F.scaled_dot_product_attention(q, k, v, attn_mask=mask, dropout_p=0.0)

        y = y.transpose(1, 2).contiguous().view(B, T, C)  # re-assemble all head outputs side by side

        # output projection
        y = self.c_proj(y)

        return y, kv_cache

经过线性层生成q,k,v,并分配到不同的注意力头
q,k,v的size均为(B,输入文本长度,注意力头数,各注意力头的维度)

词嵌入+位置编码

针对q,k,将词嵌入和位置编码结合到一起
q = apply_rope(q, rope)
k = apply_rope(k, rope)
两个步骤一样,只看一个步骤就行

def apply_rope(x: torch.Tensor, rope_cache: RoPECache) -> torch.Tensor:
    # truncate to support variable sizes
    T = x.size(1)
    rope_cache = rope_cache[:T]

    # cast because the reference does
    xshaped = x.float().reshape(*x.shape[:-1], -1, 2)
    rope_cache = rope_cache.view(1, xshaped.size(1), 1, xshaped.size(3), 2)
    x_out2 = torch.stack(
        [
            xshaped[..., 0] * rope_cache[..., 0] - xshaped[..., 1] * rope_cache[..., 1],
            xshaped[..., 1] * rope_cache[..., 0] + xshaped[..., 0] * rope_cache[..., 1],
        ],
        -1,
    )

    x_out2 = x_out2.flatten(3)
    return x_out2.type_as(x)

输入:词嵌入,位置编码
词嵌入维度:(B,输入文本长度,注意力头数,各注意力头的维度)

位置编码维度:(输入文本长度,theta长度,sin/cos)

词嵌入和位置编码分别review
词嵌入:(B,输入文本长度,注意力头数,各注意力头的维度)-->
(B,输入文本长度,注意力头数,各注意力头的维度/2(theta长度),2(sin/cos))

位置编码:(输入文本长度,theta长度,sin/cos)-->
(1(不是batch_size,单单强制为1),输入文本长度,1(强制为1),各注意力头的维度/2(theta长度),2(sin/cos))

提取正弦和余弦
xshaped[..., 0]xshaped[..., 1] 分别表示 xshaped 中正弦和余弦的值。
rope_cache[..., 0]rope_cache[..., 1] 同样表示 rope_cache 中的正弦和余弦值。

计算旋转通过旋转公式实现了在 xshaped 的每个元素上应用 rope_cache 中存储的旋转信息。
实部 = xshaped[...,0] ∗ ropecache[...,0] − xshaped[...,1] ∗ ropecache[...,1]
虚部 = xshaped[...,1] ∗ ropecache[...,0] + xshaped[...,0] ∗ ropecache[...,1]

Stack 操作最终的 torch.stack 操作将计算得到的实部和虚部按最后一个维度(-1)组合成一个新的张量。这样,输出张量 x_out2 的大小将是 (B,输入文本长度,注意力头数,各注意力头的维度/2(theta长度),2(sin/cos))

xshaped = x.float().reshape(*x.shape[:-1], -1, 2)
rope_cache = rope_cache.view(1, xshaped.size(1), 1, xshaped.size(3), 2)
x_out2 = torch.stack(
        [
            xshaped[..., 0] * rope_cache[..., 0] - xshaped[..., 1] * rope_cache[..., 1],
            xshaped[..., 1] * rope_cache[..., 0] + xshaped[..., 0] * rope_cache[..., 1],
        ],
        -1,)

最后,将x_out2 resize为(B,输入文本长度,注意力头数,各注意力头的维度),和输入向量x的形状一致

更新kv_cache
k = k.transpose(1, 2)  # (B, nh, T, hs)
q = q.transpose(1, 2)  # (B, nh, T, hs)
v = v.transpose(1, 2)  # (B, nh, T, hs)

if kv_cache is not None:
    cache_k, cache_v = kv_cache
    # check if reached token limit
    if input_pos[-1] >= max_seq_length:
        input_pos = torch.tensor(max_seq_length - 1, device=input_pos.device)
        # shift 1 position to the left
        cache_k = torch.roll(cache_k, -1, dims=2)
        cache_v = torch.roll(cache_v, -1, dims=2)
    k = cache_k.index_copy(2, input_pos, k)
    v = cache_v.index_copy(2, input_pos, v)
    kv_cache = k, v

1.将k,v resize为和kv_cache相匹配的形状(B,注意力头数,输入文本长度,各注意力头的维度)

2.将 k或v 的值放入 cache_k 中 input_pos 指定的索引位置,更新kv_cache (B,注意力头数,本次的最大文本长度,各注意力头的维度)

计算注意力

y = F.scaled_dot_product_attention(q, k, v, attn_mask=mask, dropout_p=0.0)

# causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)
#  att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
#  att = att.masked_fill(mask[:,:,:T,:T] == 0, float('-inf'))
#  att = F.softmax(att, dim=-1)
#  y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)

计算缩放点积注意力(Scaled Dot-Product Attention),利用了高效的 Flash Attention CUDA 内核以加速计算

q的size为(B,注意力头数,输入文本长度,各注意力头的维度)
k,v的size为(B,注意力头数,本次的最大文本长度,各注意力头的维度)
mask的size为(1,1,输入文本长度,本次输入的最大文本长度)

y = y.transpose(1, 2).contiguous().view(B, T, C)

转换维度,将各注意力头的维度连接到一起后,输出维度为(B,输入文本长度,embedding维度)

经过线性层维度不变,返回本次循环Block的自注意力计算结果y和kv_cache

y = self.c_proj(y)
return y, kv_cache

再次经过RMSNorn归一化
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # NOTE: the original RMSNorm paper implementation is not equivalent
        # norm_x = x.norm(2, dim=self.dim, keepdim=True)
        # rms_x = norm_x * d_x ** (-1. / 2)
        # x_normed = x / (rms_x + self.eps)
        norm_x = torch.mean(x * x, dim=self.dim, keepdim=True)
        x_normed = x * torch.rsqrt(norm_x + self.eps)
        return self.scale * x_normed

输出zise为 (B,输入文本长度,embedding维度)

经过MLP层
x = F.silu(self.c_fc1(x)) * self.c_fc2(x)

分为F.silu(self.c_fc1(x))和self.c_fc2(x)两个部分相乘

维度大小的选取查看前面模型初始化的mlp

激活函数ReLU和SiLU的区别-CSDN博客
激活函数 Relu,Gelu,Mish,SiLU,Swish,Tanh,Sigmoid_gelu和silu-CSDN博客
大模型基础|激活函数|从ReLU 到SwiGLU
torch.nn.functional.silu — PyTorch 2.4 documentation

\text{SiLU}(x) = x \cdot \sigma(x),其中\sigma(x) = \frac{1}{1 + e^{-x}}

*: 这里的乘法是元素级的,即逐元素相乘。这意味着 F.silu(self.c_fc1(x)) 和 self.c_fc2(x) 的每个对应元素相乘,产生一个新的张量。 

最后经过线性层后将11008维,还原为4096维(embedding维度)

本次循环的transfomer Block结束,进入下一次循环

 RMSNorm归一化

x = self.transformer.ln_f(x)

经过32层transfomer Block后,再经过RMSNorm层进行归一化

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # NOTE: the original RMSNorm paper implementation is not equivalent
        # norm_x = x.norm(2, dim=self.dim, keepdim=True)
        # rms_x = norm_x * d_x ** (-1. / 2)
        # x_normed = x / (rms_x + self.eps)
        norm_x = torch.mean(x * x, dim=self.dim, keepdim=True)
        x_normed = x * torch.rsqrt(norm_x + self.eps)
        return self.scale * x_normed

网络输出

logits = self.lm_head(x)

输出维度为词库的大小,将输出的logits经过归一化计算选择词库中各个词的概率

生成下一分词的循环过程

temperature

温度越低,结果的差距越大,会使概率分布更加尖锐,从而使得模型更倾向于选择最高概率的类别。

选取前topk

        if top_k is not None:
            v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
            logits = torch.where(logits < v[[-1]], -float("Inf"), logits)

torch.topk(input, num)

返回input中指定的前num个值和对应的索引

torch.where(condition, x, y)

根据条件张量选择输出。对于每个位置,condition 为 True 时取 x,为 False 时取 y
v[[-1]] 选择 v 中的最后一个值,这个值是第 k 大的值。使用双重方括号是为了保持 v[[-1]] 的维度与 logits 相匹配。

选择当前时刻网络生成的分词

probs = torch.nn.functional.softmax(logits, dim=-1)
idx_next = torch.multinomial(probs, num_samples=1).to(dtype=dtype)

torch.nn.functional.softmax(input, dim)

对 input 张量的指定维度 dim 应用 softmax 操作 
logits 转化为一个概率分布,每个值在 [0, 1] 之间,总和为 1。这样,模型的输出可以解释为各类别的预测概率

torch.multinomial(input, num_samples)

这个函数从 input 张量中根据给定的概率分布进行随机采样。
input: 在这里是 probs,是一个概率分布张量,通常表示每个类别的预测概率。
num_samples: 设定要从概率分布中采样的数量。num_samples=1 表示只需要采样一个类别

将新生成的索引加入到输入文本中

input_pos = input_pos[-1:] + 1

if idx.device.type == "xla":
    xm.mark_step()

# concatenate the new generation
idx = idx.index_copy(0, input_pos, idx_next)
# if <eos> token is triggered, return the output (stop generation)
if idx_next == eos_id:
    return idx[:input_pos]  # include the EOS token

更新input_pos

idx.index_copy(dim, index, source)

dim: 这个参数指定了要更新的维度。在这里,dim=0 表示更新张量的第一维。
index: 这是一个包含索引的张量,指示 source 张量的值应该被复制到 idx 张量的哪些位置。source: 这是一个包含要插入值的张量。在这里是 idx_next,它是一个表示新值的张量

第二次循环

更新输入

根据更新的input_pos更新输入

更新旋转位置编码

rope_cache已经保存了可处理最大数据块大小对应的各个位置的旋转位置编码
根据位置直接选取,对应位置的旋转位置编码即可

更新mask

mask_cache已经保存各个位置对应的mask
根据位置直接选取对应的mask,然后根据本次的最大文本长度对mask进行截取即可

 更新词嵌入

根据文本对应索引经过embedding,生成新分词对应的词嵌入

进入transfomer隐藏层 

对新生成的分词进行RMSNorm

生成新的q,k,v

size均为(B,注意力头数,文本长度,各注意力头的维度)

 更新k,v及kv_cache

size:(B,注意力头数,本次的最大文本长度,各注意力头的维度)

 生成当前位置的注意力分数

生成第二次循环的隐藏层输出 


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2129689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5 模拟——59. 螺旋矩阵II ★★

5 模拟 59. 螺旋矩阵II 给你一个正整数n,生成一个包含 1 到 n2所有元素,且元素按顺时针顺序螺旋排列的nn正方形矩阵 matrix 。 示例1: 输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]] 示例2: 输入:n = 1 输出:[[1]] 算法设计 本题与上一题【54. 螺旋矩阵】不同,上一…

1、https的全过程

目录 一、概述二、SSL过程如何获取会话秘钥1、首先认识几个概念&#xff1a;2、没有CA机构的SSL过程&#xff1a;3、没有CA机构下的安全问题4、有CA机构下的SSL过程 一、概述 https是非对称加密和对称加密的过程&#xff0c;首先建立https链接需要经过两轮握手&#xff1a; T…

redis基本数据结构-hash

这里写自定义目录标题 1. redis的数据结构hash1.1 Hash 数据结构的特点1.2 常见命令1.3 适用示例 2. 常见业务场景2.1 用户信息存储2.1.1 场景2.1.2 优势2.1.3 解决方案2.1.4 代码实现 2.2 购物车管理2.2.1 背景2.2.2 优势2.2.3 解决方案2.2.4 代码实现 3. 注意事项&#xff1a…

使用虚拟信用卡WildCard轻松订阅POE:全面解析平台功能与订阅方式

POE&#xff08;Platform of Engagement&#xff09;是一个由Quora推出的人工智能聊天平台&#xff0c;汇集了多个强大的AI聊天机器人&#xff0c;如GPT-4、Claude、Sage等。POE提供了一个简洁、统一的界面&#xff0c;让用户能够便捷地与不同的AI聊天模型进行互动。这种平台的…

Shadertoy和desmos用来快速图像化辅助计算的好工具

Desmos适用场景解直线方程例子 Shadertoy是一个专门通过shader片段利用gpu像素着色的工具。每一帧都会执行显示区域每个像素点的着色。默认片段坐标是左下角(0,0)到右上角(像素分辨率大小)。有网页版&#xff0c;也有vscode插件版。插件版更方便.如果要验证一些图像化的计算。…

MyBatis-Plus分页查询、分组查询

目录 准备工作1. 实体类2. Mapper类3. 分页插件4. 数据 分页查询1. 使用条件构造器2. 使用自定义sql 分组查询1. 分组结果类2. 自定义sql3. 测试类 准备工作 1. 实体类 对地址字段address使用字段类型转换器&#xff0c;将List转为字符串数组保存在数据库中 package com.exa…

(web自动化测试+python)1

一.UI自动化测试介绍 1.测试化理论 UI就是指的是用户接口&#xff0c;指的是用户与电脑的接口&#xff0c;是用户界面 UI不仅仅指的是web&#xff0c;还可以指代app 我们为什么要进行自动化&#xff1f; 大量版本的回归 当新的功能出现&#xff0c;复测之间的--我们叫做回归&am…

《Diffusion Models Without Attention》CVPR2024

摘要 这篇论文探讨了在高保真图像生成领域&#xff0c;去噪扩散概率模型&#xff08;Denoising Diffusion Probabilistic Models, DDPMs&#xff09;的重要性。尽管DDPMs在捕捉复杂视觉分布方面表现出色&#xff0c;但在高分辨率图像生成上面临显著的计算挑战。现有的方法&…

动物目标检测——基于YOLOv5和树莓派4B平台

目标检测在计算机视觉领域中具有重要意义。YOLOv5&#xff08;You Only Look One-level&#xff09;是目标检测算法中的一种代表性方法&#xff0c;以其高效性和准确性备受关注&#xff0c;并且在各种目标检测任务中都表现出卓越的性能。本文将详细介绍如何在性能更强的计算机上…

java实习生第一次被分配需求——完成需求的大概流程

一、分配需求后第一步&#xff0c;首先是把项目跑起来 在我进入公司一两个星期之后&#xff08;基本熟悉了公司的框架&#xff09;&#xff0c;就被我所在的开发小组的某个大哥分派了一个需求&#xff0c;然后他给我发了一个git地址&#xff0c;以及一个git的分支&#xff08;…

知名专家曹启富主任:冠心病低龄化?早预防早受益,守护心脏从日常做起

冠心病&#xff0c;这一曾被视为中老年疾病的代表&#xff0c;如今正悄然向更年轻的人群逼近。冠心病多发生于40岁以上的年龄段&#xff0c;但近年来&#xff0c;其发病低龄化的趋势日益明显&#xff0c;根据数据显示&#xff0c;我国城市人群15岁以及15岁以上的人口&#xff0…

STM32G474之TAMPALRM输出

TAMPALRM输出源是指“RTC唤醒”、“RTC报警A”和“RTC报警B”输出&#xff0c;可以配置从RTC_OU1(PC13)或RTC_OUT2(PB2)输出&#xff0c;而OUT2EN用来决定从哪个引脚输出。 1、TAMPALRM输出原理见下表&#xff1a; 若不看CALIB输出&#xff0c;可以简化如下表&#xff1a; 2、 …

【API Testing and Development with Postman 2nd_001】关于本书

译者按 今天又淘到一本介绍 Postman 的宝藏级小册子&#xff0c;非常适合想进一步了解 API 接口测试的朋友们。本书最大的特点就是手把手教学。想当年第 1 版问世时&#xff0c;初出茅庐的我随便拣了书中一两招&#xff0c;就能轻松搞定工作中五花八门的 API 疑难杂症。只是当时…

监听键盘事件

问题&#xff1a;点击输入框弹出键盘遮挡文字 需求&#xff1a;点击输入框键盘弹起&#xff0c;点击别处键盘回收&#xff0c;输入框回到原来状态&#xff0c; 解决办法&#xff1a; 1.采用占位的思想&#xff08;隐藏&#xff09;&#xff0c;文本框控制采用焦点控制&#…

多态的概念

多态 所谓的多态其实就是多种形态&#xff0c;它又被分为编译时多态(静态多态) 和 运行时多态(动态多态)。 静态的多态其实就是之前的模版和函数重载&#xff0c;今天我们主要讲动态的多态。所谓的动态多态其实就是相同的函数&#xff0c;完成不同的功能。 这就实现了明明都是…

C#搭建WebApi服务

1&#xff0c;OWIN的介绍 OWIN 的全称是 "Open Web Interface for .NET"&#xff0c; OWIN 在 .NET Web 服务器和 .NET Web 应用之间定义了一套标准的接口&#xff0c; 其目的是为了实现服务器与应用之间的解耦&#xff0c;使得便携式 .NET Web 应用以及跨平台的愿望…

MongoDB事务机制

事务机制 1.事务概念 在对数据的操作的过程中&#xff0c;涉及到一连串的操作&#xff0c;这些操作如果失败&#xff0c;会导致我们的数据部分变化了&#xff0c;部分没变化。这个过程就好比如你去吃早餐&#xff0c;你点完餐了&#xff0c;并且吃完早餐了&#xff0c;没付钱你…

ES6标准---【五】【看这一篇就够了!!!】

目录 ES6以往文章 箭头函数的基本用法 箭头函数的用处 简化回调函数 rest参数与箭头函数结合 箭头函数使用注意点 this指向的问题 其它不存在的变量 不能使用call()、apply()、bind()方法改变this的指向 箭头函数不适用场合 定义对象时&#xff0c;对象方法内部包含…

信创环境下源代码防泄露解决方案

在当今数字化时代&#xff0c;信息安全已成为企业生存与发展的基石&#xff0c;尤其是在信息技术应用创新&#xff08;信创&#xff09;环境下&#xff0c;数据保护更是被提升至前所未有的高度。SDC沙盒防泄露系统以其独特的技术架构和卓越的安全性能&#xff0c;在信创环境中构…

ES6标准---【六】【学习ES6标准看这一篇就够了!!!】

目录 以往ES6文章 前言 对象属性的简洁表示法 一个实际例子 简介写法在打印对象时也很有用 注意 对象属性名表达式 用表达式做属性名 用表达式定义方法名 注意 对象方法的name属性 对象属性的可枚举性和遍历 可枚举性 属性的遍历 属性比那里次序规则 super关键…