LLM - Transformer LLaMA2 结构分析与 LoRA 详解

news2024/10/6 12:35:01

目录

一.引言

二.图说 LLM

1.Transformer 结构

◆ Input、Output Embedding

◆ PositionEmbedding

◆ Multi-Head-Attention

◆ ADD & Norm

◆ Feed Forward

◆ Linear & Softmax

2.不同 LLM 结构

◆ Encoder-Only

◆ Encoder-Decoder

◆ Decoder-Only 

3.LLaMA-2 结构

◆ Input Embedding

◆ RMSNorm

◆ RoPE

◆ Attention

◆ SwiGLU

◆ MLP

三.数说 LoRA

1.LLaMA-2 7B 原始参数

2.LLaMA-2 7B LoRA 参数

3.Peft Add LoRA Adapter

◆ LoraModel

◆ add_adapter

◆ _find_and_replace

◆ mark_only_lora_as_trainable

◆ Linear 

◆ update_layer

◆ reset_lora_parameters

四.总结


一.引言

 大模型进行 Lora 微调时,需要指定 lora_target,以 LLama2 为例,官方指出可以使用下述层:

--lora_target 'q_proj,v_proj,k_proj,o_proj,gate_proj,down_proj,up_proj'

看过 Transformer 的同学对 Q/K/V 肯定不陌生,但是 gate、up、down 又是什么鬼;还有 Lora 指定不同 lora_target 时可训练参数会调整,具体每个地方增加了多少参数也没搞明白。结合上述问题,博主本文对 Transformer、LLaMA-2 以及 LoRA 的一些基础知识进行分享~

二.图说 LLM

1.Transformer 结构

左侧为多个 Encoder Block 叠加,右侧为多个 Decoder Block 叠加,二者合在一起为整体的 Transformer 结构。关于 Transformer 这里简单介绍其各个结构,更具体的详解会在文末给出参考链接,大家可以深入学习。

Input、Output Embedding

NLP 最基本的 Embedding 层,可以通过 Word2vec、Bert 等预训练方式获取 Token_Id 的 Embedding,也可以端到端训练得到,当我们输入原始文本后,Text 经过 Tokenizer 处理得到 Input_ids,Input_ids 再经过 Embedding 层就获得原始的词向量了。

PositionEmbedding

除了原始的单词 Embeeding 外,Transformer 还引入了 position Embedding 表示单词在句子中的位置。实验表明,引入 Position Embedding 比不引入 Position Embedding 的效果更好,一般 PositionEmbedding 用于表示单词在句子中的绝对位置或相对位置。Position Embedding 维度与上面的单词 Embeeding 维度相同,可以训练也可以使用公式计算,Transformer 中采用了后者,计算公式为:

- pos 代表单词在句子中的位置 

- d 标识 Position Embedding 的维度

- 2i、2i+1 标识偶数和奇数的维度

计算得到 PE 后与原 Embeeding 相加进入后续逻辑。

Tips:

这里奇数和偶数位置的编码方法使用了正弦 sin 和余弦 cos 的不同的函数。这里奇数位置和偶数位置的区别在于他们所应用的正弦和余弦函数的角频率,通过调整角频率,可以使得奇数和偶数位置的编码方式有所不同。通过调整角频率,可以使得奇数和偶数位置编码的值在编码空间呈现不同的变化模式。这样的设计旨在为相邻位置提供不同的位置编码,以增加模型对序列中位置信息的敏感度。

Multi-Head-Attention

上图为 Multi-Head Attention [多头注意力] 结构,在计算的时候,会将 Embedding + Position Embeeding 相加的结果,分别经过 Q-Query-Linear、K-Key-Linear 和 V-Value-Linear 进行一次线性变换送入后续逻辑。

Scaled Dot-Product 负责将 Linear 转化的 Q/K/V 进行 Attention 计算,其中 Q/K MatMul 乘法后为了防止内积过大,会除以 \sqrt{d_k} 进行 Scale 操作,最后通过 Softmax 归一化并与 Value 向量相乘得到最后的输出。

Multi-Head Attention 旨在学习向量不同特征空间的表现,所以 Multi-Head Attention 会有多个 Linear 和 Scaled Dot-Product,最终将多个 head 对应的 Attention 结果 Concat 再过一层 Linear 传到下一层作为 Input。

ADD & Norm

 ADD & Norm 看字面意思就知道它是由 ADD 和 Norm 两个部分组成,其计算公式如下:

其中 X 代表上一步的输出,从图中可以看到 Feed Forward 和 Multi-Head Attention 都会输出到 ADD & Norm 层,这里结构与 RNN 的残差网络类似,将原始输入与上一步的输出相加。结果相加后通过 Norm 层就行归一化加速收敛。

Feed Forward

Feed Forward 层采用两层全连接,第一层为 Relu 第二层不使用激活函数。

Linear & Softmax

经过多层 Block 的堆叠,终于到达最终的 Linear 层,使用 LLM 我们常看到的 lm_head 就是一种 Linear,该层负责生成语言模型的输出。它是模型架构中的最后一层,用于将 Transformer 编码器的输出转化为预测下一个词或生成文本的概率分布。

具体来说,Linear 层会对编码器的输出进行线性变换,将其映射到与词汇表大小相同的向量空间。这样做的目的是为了预测下一个词的概率分布。通常,线性变换后的输出经过 softmax 函数处理,以获得归一化的概率分布,其中每个词的概率表示在给定前文条件下该词是下一个出现的可能性。

在训练阶段,可以使用交叉熵损失函数来比较模型输出的概率分布与实际的下一个词的标签,并通过反向传播更新模型参数。而在生成阶段,可以根据概率分布进行采样,以生成连贯的文本。

2.不同 LLM 结构

最新的大语言模型基本都是基于上述 Transformer 结构进行拓展:

Encoder-Only

▲ 包含一个单独的编码器,没有显式的解码器部分。
▲ 主要用于 AutoEncoder 等任务,其主要学习数据的压缩标识

Encoder-Only 学习的压缩表示可以用于特征提取,数据降维等任务,具有较好的泛化能力。最常见的就是 Bert,这里所有输出 token 都能看到过去和未来的所有输入 Token,因此对于 NLU [Natural Language Understanding] 自然语言文本理解和分析的任务很友好。

Tips 常见的 NLU 任务如下:

- 语句分类 [Sentence Classification] 根据给定文本判断其标签,如情感分类、垃圾邮件分类

- 意图识别 [Intent Recognition] 识别用户输入表的的意图或目的,如天气、餐厅查询

- 实体识别 [Named Entity Recognition] 从文本中识别标注实体,如人名、地点识别

- 信息抽取 [Infomation Extraction] 从文本中提取特定含义信息,如从新闻提取摘要

- 关系抽取 [Relation Extraction] 识别本文与实体的关系,如 "Apple" 与 "Steve Jobs" 的关系

- 问答系统 [Question Answering] 为给定问题找到答案,如阅读理解,知识库问答。

这些任务旨在让计算机能够理解和处理自然语言文本,从而支持各种应用场景,如智能助手、虚拟客服、搜索引擎等。NLU 任务的目标是将自然语言转化为结构化的表示形式,以便计算机能够更好地理解和响应用户的需求。

Encoder-Decoder

▲ 由一个编码器和一个解码器组成,常用于序列到序列(Sequence-to-Sequence)任务,如机器翻译、语音识别等。
▲ 编码器将输入序列编码为固定长度的向量,解码器从该向量生成输出序列。

Encoder-Decoder 适用于需要输入和输出之间存在复杂映射的任务,能够处理变长序列,并且可以用于翻译、生成等多种任务。GLM、T5、Bart 都已普遍应用。

Decoder-Only 

 Decoder-Only 相比前两个分支明显枝繁叶茂,硕果累累,最出名的就算是 GPT 家族了。 

▲ 只包含解码器部分,没有显式的编码器。通常用于条件生成任务,其中给定一些条件信息,模型通过解码器生成相应的输出。
▲ 与 Encoder 相比二者 Multi-Head Attention 相差一个 MASK,会导致前文无法使用后文未知的消息,第二个 Multi-Head Attention 则与 Encoder 相似。

实际应用中,Decoder-Only 适用于从条件信息中生成输出的任务,例如图像描述生成,文本生成。常见的 chat 模型就基于 Decoder-Only。

3.LLaMA-2 结构

LLaMA-2 采用 Decoder-Only 的架构,由 Attention 和 MLP 层堆叠而成,Lora 微调 LLaMA-2 时这些 target 对应的结构也能在图中找出了。

--lora_target 'q_proj,v_proj,k_proj,o_proj,gate_proj,down_proj,up_proj'

与传统 Transformer 结构相比,其主要做了如下修改:

RMSNorm - 传统结构 Norm 放在 Att 后,这里将 Norm 前置

RoPE 编码 - 更换了 Position Embedding 的编码方式

MLP 层更新 - 增加了 UP 和 Donw 的线性层并使用 SiLU 激活 

与 LLaMA-1 相比,其增加了一倍 Context Length 达到 4096,使其处理长文本能力更强:

Input Embedding

这里与常规 transformer 的输入层逻辑一致。

RMSNorm

Bert、GPT 常用的均值方差归一化 LayerNorm 为:

y = \frac{x-Mean(x)}{\sqrt{Var(x)+\varepsilon }}*W+B

RMS 意为 root mean square 均方根,用于衡量一组数值的平均值大小:

RMS(x)=\sqrt{\frac{1}{n}\sum x_i^2}

RMSNorm 发现 LayerNorm 的取消中心偏移即 Mean(x) 效果激活不变且可以提高计算效率,最终公式为:

RMSNorm=\frac{x}{\sqrt{RMS(x)+\varepsilon )}}*W

LLaMA-2 在 Embedding 和 Attention Output 后都引入了 RMSNorm。

RoPE

对于 Q-Query、K-Key 第 m 个位置的向量 q,通过下述方法得到 Position Embedding:

d 为 Embedding 的维度,θ 为预设的非零常数,RoPE 旋转位置嵌入的实现过程如下图:

Attention

 Attention 部分的 QKV 与先前相似,除了引入 RoPE 旋转位置嵌入外,还引入了 Causal Mask:

BART原理简介与代码实战_Congqing He的博客-CSDN博客

Causal Mask 的引入使得前文无法获得下文的知识,例如 'I love eating lunch' 中 love 只能获得 'I' 和 'love' 的信息。剩下就是常规的 Attention 与 Softmax 操作,最后的 o 代表 Output,其参数维度与 Q/K/V 的 Linear 相同。

SwiGLU

SwiGLU 又称 SiLU,与 GeGLU 相比用 Swish 替代了 GeLU:

其中 ⓧ 代表逐元素乘法,Swish 公式为:

Swish_\beta (x)=x\sigma (\beta x)

其中 σ 代表 sigmoid 函数,其示意图如下: 

从图像可以看出 SiLU 更像是 ReLU 的平滑版本。 

MLP

这里 MLP 的表达式为:

donw(up(x) \times SiLU(gate(x)))

其中 up、down 与 gate 是三个维度相同的 Linear 层。

三.数说 LoRA

1.LLaMA-2 7B 原始参数

    model = AutoModelForCausalLM.from_pretrained(
        base_model,
        config=config,
        torch_dtype=torch.float16,
        low_cpu_mem_usage=True,
        trust_remote_code=True,
        revision='main'
    )

    model_vocab_size = model.get_input_embeddings().weight.size(0)
    tokenzier_vocab_size = len(tokenizer)
    print(model.get_input_embeddings().weight.size())
    print(f"Vocab of the base model: {model_vocab_size}")
    print(f"Vocab of the tokenizer: {tokenzier_vocab_size}")

    for name,param in model.named_parameters():
        print(name, param.numel(), param.requires_grad)

这里我们直接加载 LLaMA-2 7B 模型,看看每一层参数的情况:

model.embed_tokens.weight 131072000 True
model.layers.0.self_attn.q_proj.weight 16777216 True
model.layers.0.self_attn.k_proj.weight 16777216 True
model.layers.0.self_attn.v_proj.weight 16777216 True
model.layers.0.self_attn.o_proj.weight 16777216 True
model.layers.0.mlp.gate_proj.weight 45088768 True
model.layers.0.mlp.down_proj.weight 45088768 True
model.layers.0.mlp.up_proj.weight 45088768 True
model.layers.0.input_layernorm.weight 4096 True
model.layers.0.post_attention_layernorm.weight 4096 True
                       ...
model.layers.31.self_attn.q_proj.weight 16777216 True
model.layers.31.self_attn.k_proj.weight 16777216 True
model.layers.31.self_attn.v_proj.weight 16777216 True
model.layers.31.self_attn.o_proj.weight 16777216 True
model.layers.31.mlp.gate_proj.weight 45088768 True
model.layers.31.mlp.down_proj.weight 45088768 True
model.layers.31.mlp.up_proj.weight 45088768 True
model.layers.31.input_layernorm.weight 4096 True
model.layers.31.post_attention_layernorm.weight 4096 True
model.norm.weight 4096 True
lm_head.weight 131072000 True

这里可以看到 LLaMA-2 7B 共堆叠了 32 个 Block,其中有我们 lora_target 熟悉的层:

--lora_target 'q_proj,v_proj,k_proj,o_proj,gate_proj,down_proj,up_proj'

通过上面 LLaMA-2 结构的分析,相信这几个层在哪我们起码已经知道了,下面看看每一个 Block 的参数量:

前面已经介绍了 LLaMA-2 将 Context Length 扩展至 4096,这里  layernorm 参数量为 4096,一个单独 Block 的参数量为 2 亿左右。再看下整体的参数量:

这里打印得到的 Token 数为 32000,32000 * 4096 = 131072000 与 Input Embedding 和 lm_head 层的参数可以匹配。32 个 Decoder 再加上 Norm 和最后的 lm_head,总参数 6.94B ≈ 7B。

2.LLaMA-2 7B LoRA 参数

    lora_config = LoraConfig(
        task_type=TaskType.CAUSAL_LM,
        inference_mode=False,
        r=8,
        lora_alpha=32,
        lora_dropout=0.1,
        target_modules=["q_proj","v_proj","k_proj","o_proj","gate_proj","down_proj","up_proj"]
    )
    model = get_peft_model(model, lora_config)
    for name,param in model.named_parameters():
        print(name, param.numel(), param.requires_grad)

增加 LoraConfig 结合 peft 获取 LoRA 后的模型,这里秩 r = 8,再看下每层的情况:

base_model.model.model.embed_tokens.weight 131072000 False
base_model.model.model.layers.0.self_attn.q_proj.weight 16777216 False
base_model.model.model.layers.0.self_attn.q_proj.lora_A.default.weight 32768 True
base_model.model.model.layers.0.self_attn.q_proj.lora_B.default.weight 32768 True
base_model.model.model.layers.0.self_attn.k_proj.weight 16777216 False
base_model.model.model.layers.0.self_attn.k_proj.lora_A.default.weight 32768 True
base_model.model.model.layers.0.self_attn.k_proj.lora_B.default.weight 32768 True
base_model.model.model.layers.0.self_attn.v_proj.weight 16777216 False
base_model.model.model.layers.0.self_attn.v_proj.lora_A.default.weight 32768 True
base_model.model.model.layers.0.self_attn.v_proj.lora_B.default.weight 32768 True
base_model.model.model.layers.0.self_attn.o_proj.weight 16777216 False
base_model.model.model.layers.0.self_attn.o_proj.lora_A.default.weight 32768 True
base_model.model.model.layers.0.self_attn.o_proj.lora_B.default.weight 32768 True
base_model.model.model.layers.0.mlp.gate_proj.weight 45088768 False
base_model.model.model.layers.0.mlp.gate_proj.lora_A.default.weight 32768 True
base_model.model.model.layers.0.mlp.gate_proj.lora_B.default.weight 88064 True
base_model.model.model.layers.0.mlp.down_proj.weight 45088768 False
base_model.model.model.layers.0.mlp.down_proj.lora_A.default.weight 88064 True
base_model.model.model.layers.0.mlp.down_proj.lora_B.default.weight 32768 True
base_model.model.model.layers.0.mlp.up_proj.weight 45088768 False
base_model.model.model.layers.0.mlp.up_proj.lora_A.default.weight 32768 True
base_model.model.model.layers.0.mlp.up_proj.lora_B.default.weight 88064 True
base_model.model.model.layers.0.input_layernorm.weight 4096 False
base_model.model.model.layers.0.post_attention_layernorm.weight 4096 False
                      ...
base_model.model.model.norm.weight 4096 False
base_model.model.lm_head.weight 131072000 False

依旧是 32 个 Decoder 的堆叠,可以看到在我们制定的 target_model 上都增加了对应的 LoRA weight,由于我们的 Context Length x r = 4096 x 8 = 32768 可得矩阵 A 的维度,通过 lora_B 的 88604 维度也可以反推出,88604 / 8 = 11008 矩阵 B 的维度 8 x 11008,不过看原图中 LoRA 的维度是属于 R^{ d\times d},这里有一点疑问,大家可以提供下思路:

单个 Decoder 参数量:

参数量为 2 亿,可以看到 r = 8 较低时,整体训练的参数量占比很低,我们计算下 r = 8 时全部可训练参数的占比:

624640 * 32 [32 个 Decoder 的 Lora 参数量] / (6940798976 [LLaMA-2 7B 总参数量] + 624640 * 32) = 0.002871583154398789,只占总参数比的 0.287%,所以一直都说 LoRA 可以有效减少参数训练量。同理,根据上面的 API 和算法,我们可以计算不同秩 r 对应的可选参数量并打印:

3.Peft Add LoRA Adapter

代码版本为 peft==0.4.0,类位置为:src/peft/tuners/lora.py

LoraModel

class LoraModel(torch.nn.Module):
    """
    Creates Low Rank Adapter (Lora) model from a pretrained transformers model.
    """

    def __init__(self, model, config, adapter_name):
        super().__init__()
        self.model = model
        self.forward = self.model.forward
        self.peft_config = config
        self.add_adapter(adapter_name, self.peft_config[adapter_name])

        # transformers models have a .config attribute, whose presence is assumed later on
        if not hasattr(self, "config"):
            self.config = {"model_type": "custom"}

LoraModel 初始化除了获取原始的基模型和 peft_config 外,执行了 add_adapter 添加 LoRA 层的逻辑,下面我们看下 add_adapter 方法主要实现什么功能。

add_adapter

    def add_adapter(self, adapter_name, config=None):
        if config is not None:
            model_config = getattr(self.model, "config", {"model_type": "custom"})
            if hasattr(model_config, "to_dict"):
                model_config = model_config.to_dict()

            config = self._prepare_lora_config(config, model_config)
            self.peft_config[adapter_name] = config
        self._find_and_replace(adapter_name)
        if len(self.peft_config) > 1 and self.peft_config[adapter_name].bias != "none":
            raise ValueError(
                "LoraModel supports only 1 adapter with bias. When using multiple adapters, set bias to 'none' for all adapters."
            )
        mark_only_lora_as_trainable(self.model, self.peft_config[adapter_name].bias)
        if self.peft_config[adapter_name].inference_mode:
            _freeze_adapter(self.model, adapter_name)

这里 config 配置部分我们先忽略,主要注意这两行:

_find_and_replace -> 寻找并替换

mark_only_lora_as_trainable -> 标记 lora 层可训

_find_and_replace

    def _find_and_replace(self, adapter_name):
        lora_config = self.peft_config[adapter_name]
        self._check_quantization_dependency()
        is_target_modules_in_base_model = False
        # 获取模型内层的 Name
        key_list = [key for key, _ in self.model.named_modules()]

        for key in key_list:
        	# 检查是否为 lora_target 指定的 layer
            if not self._check_target_module_exists(lora_config, key):
                continue

            is_target_modules_in_base_model = True
            parent, target, target_name = _get_submodules(self.model, key)

            if isinstance(target, LoraLayer) and isinstance(target, torch.nn.Conv2d):
            	...
            elif isinstance(target, LoraLayer) and isinstance(target, torch.nn.Embedding):
            	...
            elif isinstance(target, LoraLayer):
            	...
            # Conv2D、Embedding 和 loraLayer 的情况忽略
            # 因为我们这里是为 Base Model 增加 LoRA Layer,所以进入 else 逻辑
            else:
                new_module = self._create_new_module(lora_config, adapter_name, target)
                self._replace_module(parent, target_name, new_module, target)

        if not is_target_modules_in_base_model:
            raise ValueError(
                f"Target modules {lora_config.target_modules} not found in the base model. "
                f"Please check the target modules and try again."
            )

这里先用 model.named_modules 获取 Base Model 全部层的 name,例如 layers.x.mlp.up_proj,随后遍历每一个 name 是否属于 lora_target 所指范畴,随后根据 lora_config 调用_create_new_module 获取新的 Lora Adapter,最后执行 _replace_module。如果 target_modules 不存在于 base_model,会抛出异常。例如我们将 Baichuan 的 'W_pack' 传给 LLaMA-2 就会抛出该异常。

 _create_new_module

根据 in_features 和 out_features 与 bias 构建 Linear 层。

new_module = Linear(adapter_name, in_features, out_features, bias=bias, **kwargs)

 _replace_module

将 old_model 的 weight 和 bias 赋值到 new_model,并 to_device 将 new_model 分配到 old_model 对应的 device 上。

    def _replace_module(self, parent_module, child_name, new_module, old_module):
        setattr(parent_module, child_name, new_module)
        new_module.weight = old_module.weight
        if hasattr(old_module, "bias"):
            if old_module.bias is not None:
                new_module.bias = old_module.bias

        if getattr(old_module, "state", None) is not None:
            new_module.state = old_module.state
            new_module.to(old_module.weight.device)

        # dispatch to correct device
        for name, module in new_module.named_modules():
            if "lora_" in name:
                module.to(old_module.weight.device)
            if "ranknum" in name:
                module.to(old_module.weight.device)

mark_only_lora_as_trainable

# had to adapt it for `lora_only` to work
def mark_only_lora_as_trainable(model: nn.Module, bias: str = "none") -> None:
    for n, p in model.named_parameters():
        if "lora_" not in n:
            p.requires_grad = False
    if bias == "none":
        return
    elif bias == "all":
        for n, p in model.named_parameters():
            if "bias" in n:
                p.requires_grad = True
    elif bias == "lora_only":
        for m in model.modules():
            if isinstance(m, LoraLayer) and hasattr(m, "bias") and m.bias is not None:
                m.bias.requires_grad = True
    else:
        raise NotImplementedError

判断 lora_ 是否在 param_name 中,随后决定是否训练。

Linear 

class Linear(nn.Linear, LoraLayer):
    # Lora implemented in a dense layer
    def __init__(
        self,
        adapter_name: str,
        in_features: int,
        out_features: int,
        r: int = 0,
        lora_alpha: int = 1,
        lora_dropout: float = 0.0,
        fan_in_fan_out: bool = False,  # Set this to True if the layer to replace stores weight like (fan_in, fan_out)
        is_target_conv_1d_layer: bool = False,
        **kwargs,
    ):
        init_lora_weights = kwargs.pop("init_lora_weights", True)

        nn.Linear.__init__(self, in_features, out_features, **kwargs)
        LoraLayer.__init__(self, in_features=in_features, out_features=out_features)
        # Freezing the pre-trained weight matrix 原始 weight Freezing
        self.weight.requires_grad = False

        self.fan_in_fan_out = fan_in_fan_out
        if fan_in_fan_out:
            self.weight.data = self.weight.data.T
        
        # 初始化 LoRA 参数并更新 LoRA-A LoRA-B
        nn.Linear.reset_parameters(self)
        self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights)
        self.active_adapter = adapter_name
        self.is_target_conv_1d_layer = is_target_conv_1d_layer

上面 _create_new_module 方法根据 fan_in、fan_out 创建了 Linear,这里 Linear 也是 Lora 类中实现的,继承了 nn.linear,主要关注 update_layer 方法。

update_layer

    def update_layer(self, adapter_name, r, lora_alpha, lora_dropout, init_lora_weights):
        self.r[adapter_name] = r
        self.lora_alpha[adapter_name] = lora_alpha
        if lora_dropout > 0.0:
            lora_dropout_layer = nn.Dropout(p=lora_dropout)
        else:
            lora_dropout_layer = nn.Identity()

        self.lora_dropout.update(nn.ModuleDict({adapter_name: lora_dropout_layer}))
        # Actual trainable parameters
        if r > 0:
            self.lora_A.update(nn.ModuleDict({adapter_name: nn.Linear(self.in_features, r, bias=False)}))
            self.lora_B.update(nn.ModuleDict({adapter_name: nn.Linear(r, self.out_features, bias=False)}))
            self.scaling[adapter_name] = lora_alpha / r
        if init_lora_weights:
            self.reset_lora_parameters(adapter_name)
        self.to(self.weight.device)

首先根据 lora_config 定义 lora_alpha 和 lora_dropout_ratio,如果 r > 0 则 update lora_A 和 lora_B,scaling 层根据 lora_alpha 和 r 计算,最后 reset_lora_parameters。

reset_lora_parameters

    def reset_lora_parameters(self, adapter_name):
        if adapter_name in self.lora_A.keys():
            # initialize A the same way as the default for nn.Linear and B to zero
            nn.init.kaiming_uniform_(self.lora_A[adapter_name].weight, a=math.sqrt(5))
            nn.init.zeros_(self.lora_B[adapter_name].weight)
        if adapter_name in self.lora_embedding_A.keys():
            # initialize a the same way as the default for nn.linear and b to zero
            nn.init.zeros_(self.lora_embedding_A[adapter_name])
            nn.init.normal_(self.lora_embedding_B[adapter_name])

这里 lora_A 采用 Kaiming 初始化,这是一种常用的神经网络权重初始化方法,旨在有效地初始化深度神经网络,lora_B 采用零初始化。不过 lora_A 这里与原文的正态分布初始化有所不同。

四.总结

这里整理了 Transformer 、LLM 和 LoRA 的一些基础知识,结合最后 LoRA 的参数量与代码,我们可以很轻松的得到可训的参数与参数量,同时也可以在 LoRA 的时候增加更多自定义的功能。

!!! 最后特别感谢下面大佬们的输出:

Transformer: Transformer模型详解(图解最完整版) - 知乎

LLaMA V1/2: LLaMA v1/2模型结构总览 - 知乎

LLM 基座: [Transformer 101系列] 初探LLM基座模型 - 知乎 

GLU: https://arxiv.org/pdf/2002.05202.pdf

LLaMA 2: https://arxiv.org/pdf/2307.09288.pdf

RoPE: https://arxiv.org/pdf/2104.09864.pdf

Peft: GitHub - huggingface/peft at v0.4.0

LoRA: 大模型训练——PEFT与LORA介绍_常鸿宇的博客-CSDN博客 

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

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

相关文章

在线识别文字提取,好用的方法速速收下

在现代社会,识别文字提取已经成为了一项非常重要的技能。随着网络技术的不断发展,现在我们已经可以通过在线工具来识别文字并提取出所需要的信息。本文将分享一些好用的方法和注意事项,帮助大家更好地进行在线识别文字提取。 OCR技术 OCR技术…

来了!8月12日KCC成都站线下读书会活动诚邀您参加!

设计丨朱亿钦 相关阅读 | Related Reading 历史与今天的交融:KCC杭州 Meetup 圆满完成 KCC上海第二次活动读书会圆满举办! KCC成都首次非正式闭门会圆满成功 开源社简介 开源社成立于 2014 年,是由志愿贡献于开源事业的个人成员,依…

IPTV为什么要直连光猫

最佳答案 IPTV机顶盒之所以要与光猫连接,而且必须用网线,不能用无线网络,这是因为运营商的业务模式决定的。单纯从技术层面来说,运营商的IPTV业务有组播和OTT两种模式,目前OTT模式基本被淘汰。 所谓的OTT模式&#x…

大学python题库及答案解析,大学python程序设计题库

本篇文章给大家谈谈大学python题库及答案解析,以及python期末编程题及答案,希望对各位有所帮助,不要忘了收藏本站喔。 发表时间:2020-07-07 一、填空题(15分) 使用print()函数将多个字符串’How’、’are ’…

如何使Python Docker镜像安全、快速、小巧

一、说明 在微服务领域,拥有安全、高效和紧凑的 Docker 映像对于成功部署至关重要。本博客将探讨有助于构建此类映像的关键因素,包括不以 root 用户身份运行映像的重要性、在构建映像时更新和升级包、在编写 Dockerfile 指令时考虑 Docker 的层架构&…

嵌入式虚拟仿真实验教学平台之登录注册功能使用

登录注册功能的使用 本文将介绍嵌入式虚拟仿真实验教学平台的账号如何注册以及登录账号。 注册账号 1、首先谷歌或Edge等主浏览器中输入https://app.puliedu.com/网址,然后会跳转到登录页,如下所示: 2、点击上图中框中的新注册账号,跳转…

Java【抽象类和接口】(2)【浅拷贝、深拷贝、object类】

一、Clonable 接口和深拷贝 1.clone接口的使用 注意以下几个点: import javax.jws.soap.SOAPBinding;class Person implements Cloneable{public String name;public int age;public Person(String name, int age) {this.name name;this.age age;}Overridepublic…

无需跟踪管理,数据中心也能实时监测蓄电池状态!

当今数字化时代,数据的存储和管理已成为各行各业的核心要务。数据存储设备,如服务器、网络设备以及计算机系统,不仅仅是信息的仓库,更是组织和企业运营的支柱。 蓄电池作为关键的备份电源,在电力故障发生时起到了至关重…

Hum Brain Mapp:用于功能连接体指纹识别和认知状态解码的高精度机器学习技术

摘要 人脑是一个复杂的网络,由功能和解剖上相互连接的脑区组成。越来越多的研究表明,对脑网络的实证估计可能有助于发现疾病和认知状态的生物标志物。然而,实现这一目标的先决条件是脑网络还必须是个体的可靠标记。在这里,本研究…

CI/CD—K8S 基本理解与部署

1 K8S 是什么 Kubernetes 是一款容器的编排调度工具,来源于 Google 开源的 Brog 系统。Kubernetes简称K8S,是用8代替8个字符 “ubernete” 而成的缩写,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化…

冠达管理:什么叫死亡换手率?

逝世换手率是一个用于衡量公司股份买卖的目标,其核算方式为每年公司股份由于股东逝世而换手的比率。该目标能够用于评估公司股东结构安稳性,以及更广泛的商场安稳性。在这篇文章中,我们将从多个角度来分析逝世换手率。 首先,关于一…

Day 75:通用BP神经网络 (2. 单层实现)

代码: package dl;import java.util.Arrays; import java.util.Random;/*** Ann layer.*/ public class AnnLayer {/*** The number of input.*/int numInput;/*** The number of output.*/int numOutput;/*** The learning rate.*/double learningRate;/*** The m…

web前端之JS

文章目录 介绍一、JS引入到文件1.1 嵌入到HTML文件中1.2 引入本地独立JS文件1.3 引入网络来源文件 二、JS的注释三、JS输出方式四、JS数据类型4.1 判断数据类型 typeof4.2 charAt返回指定位置的字符4.3 concat连接两个字符串4.4 substring从原字符串提取字符串并返回4.4 substr…

java动态生成excel并且需要合并单元格

java动态生成excel并且需要合并单元格 先上图看一下预期效果 集成poi <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.0.0</version> </dependency> <dependency><…

实践分享:小程序事件系统设计

微信小程序官方文档中解释说&#xff1a;事件是用于子组件向父组件传递数据&#xff0c;可以传递任意数据。 小程序开发中的事件是指视图层到逻辑层的通讯方式&#xff0c;主要是可以将用户的行为反馈到逻辑层进行处理。事件可以绑定在组件上&#xff0c;当达到触发事件&#…

java面向对象查缺

匿名对象 匿名对象只能使用一次 public class Test1 {public static void main(String[] args) {PhoneFactory p1 new PhoneFactory();p1.show(new Phone(1000,"black"));} } class PhoneFactory{public void show(Phone p){p.send();} } class Phone{private int…

Nginx代理接口访问返回404

Nginx代理接口访问返回404 一、背景 因为不同业务系统间有接口调用&#xff0c;存在跨域问题&#xff0c;为了解决同源策略&#xff0c;需要将接口通过nginx去转发&#xff0c;但是配置完后通过postman请求一直存在访问404的问题。 访问地址&#xff1a;https://a.test.com/n…

无代码集成明道云与更多应用连接

明道云是一个APaaS平台&#xff0c;可以帮助用户快速搭建个性化企业应用&#xff0c;用户不需要代码开发就能够搭建出用户体验上佳的销售、运营、人事、采购等核心业务应用&#xff0c;打通企业内部数据&#xff0c;也能够通过API和Webhook和其他系统对接。 场景描述&#xff…

Vue实战技巧:从零开始封装全局防抖和节流函数

前言 你是否曾经遇到过用户频繁点击按钮或滚动页面导致反应迟钝的问题&#xff1f;这是因为事件被连续触发&#xff0c;导致性能下降。在本文中&#xff0c;我将为大家介绍 vue 中的防抖和节流策略&#xff0c;并展示如何封装全局的防抖节流函数&#xff0c;以避免频繁触发事件…

酷开系统丨加入酷开会员,体验有趣的云逛荷兰海牙市立博物馆

夏日炎炎&#xff0c;出门是不可能的&#xff0c;还是宅在家里享受空调的吹拂吧&#xff01;如果想足不出户看展览&#xff0c;感受文化的熏陶&#xff0c;那就打开酷开系统&#xff0c;加入酷开会员&#xff0c;在云端逛逛荷兰海牙市立博物馆吧&#xff01; 荷兰海牙市立博物…