预训练和监督微调(SFT)之间的差异

目录

更详细的解释

 

预训练的要点

 

微调的重点

概括

预训练代码演示

微调代码演示

加载模型的差异

训练参数的差异

 

分布式训练实现

威震天-DeepSpeed

 

DeepSpeed

 

墨西哥钝口螈

加速


当我们谈论预训练和监督微调(SFT)之间的差异时,目标、使用的数据集和所需的 GPU 数量都是不同的。但是,如果我们要从深度学习训练的本质来解释差异,那就是:

 

预训练是随机初始化模型参数,构建模型,然后在大量未标注数据上进行训练,学习语料库的共性特征;而微调是加载预训练模型中的参数,保留预训练中学习到的共性特征,并在少量高质量标注数据上进行训练,以提升模型在特定任务上的能力和性能。 

 

上面提到的参数包括:权重、偏差、Word Embeddings、Positional Encoding、注意力机制参数等。

 

更详细的解释

 

预训练旨在使用大规模无监督数据集(例如文本语料库)学习语言的基本结构和语义特征。预训练通常涉及以下步骤:

  1. 权重的随机初始化:模型的参数,例如权重和偏差,在预训练开始时随机初始化。
  2. 大规模数据集:使用大量无监督数据进行训练。
  3. 学习一般特征:模型通过优化损失函数(例如语言模型的交叉熵损失)来学习语言的一般特征。
 
预训练的要点
  • 随机初始化:预训练开始时,所有模型参数(权重、偏差等)都是随机的。
  • 大规模数据:使用大规模无监督数据集进行训练。
  • 总体特征:模型学习语言的基本结构和语义特征,为后续任务提供良好的起点。

微调旨在使用特定于任务的数据集优化模型在特定任务上的性能。微调通常涉及以下步骤:

  1. 加载预先训练的权重:模型的权重和偏差从预先训练的模型中加载。
  2. 特定任务数据:使用特定于任务的数据集进行训练。
  3. 优化任务性能:模型通过优化损失函数来调整其参数,以提高特定任务的性能。
 
微调的重点
  • 加载预训练权重:从预训练模型中加载模型的参数,保留预训练期间学习到的一般特征。
  • 特定任务数据:使用特定于任务的数据集进行训练。
  • 任务优化:进一步调整模型的参数,以优化在特定任务上的性能。

概括

  1. 训练效率:预训练通常需要大量的计算资源和时间,因为它涉及在大规模数据集上训练所有模型参数。微调相对高效,因为它建立在预训练模型的基础上,只需要在特定任务的数据上进一步优化。
  2. 模型性能:预训练模型已经学习了通用语言特征,因此可以通过微调更快地收敛并在特定任务上表现更好。从随机初始化训练特定任务的模型通常需要更多的数据和时间,其性能可能不如预训练 + 微调方法。
  3. 应用场景:预训练模型可以作为通用的基础模型,适用于各种下游任务。通过微调可以快速适应不同的任务需求,而无需从头开始训练模型。

预训练代码演示

以 GPT-2 为例

https://huggingface.co/docs/transformers/v4.44.0/en/model_doc/gpt2#transformers.GPT2LMHeadModel

为了预训练 GPT-2,我们需要使用类GPT2LMHeadModel和GPT2Config:

 

 

<span style="background-color:#2b2b2b"><span style="color:#f8f8f2"><code class="language-applescript">config <span style="color:#00e0e0">=</span> GPT2Config<span style="color:#fefefe">(</span><span style="color:#fefefe">)</span> 
model <span style="color:#00e0e0">=</span> GPT2LMHeadModel<span style="color:#fefefe">(</span>config<span style="color:#fefefe">)</span> 
tokenizer <span style="color:#00e0e0">=</span> GPT2Tokenizer.from_pretrained<span style="color:#fefefe">(</span><span style="color:#abe338">"gpt2"</span><span style="color:#fefefe">)</span> 
tokenizer.pad_token <span style="color:#00e0e0">=</span> tokenizer.eos_token  
dataset <span style="color:#00e0e0">=</span> load_dataset<span style="color:#fefefe">(</span><span style="color:#abe338">"wikitext"</span><span style="color:#fefefe">,</span> <span style="color:#abe338">"wikitext-2-raw-v1"</span><span style="color:#fefefe">)</span> 

def tokenize_function<span style="color:#fefefe">(</span>examples<span style="color:#fefefe">)</span><span style="color:#fefefe">:</span> 
    <span style="color:#00e0e0">return</span> tokenizer<span style="color:#fefefe">(</span>examples[<span style="color:#abe338">"text"</span>]<span style="color:#fefefe">,</span> truncation<span style="color:#00e0e0">=</span>True<span style="color:#fefefe">,</span> padding<span style="color:#00e0e0">=</span><span style="color:#abe338">"max_length"</span><span style="color:#fefefe">,</span> max_length<span style="color:#00e0e0">=</span><span style="color:#00e0e0">512</span><span style="color:#fefefe">,</span> return_special_tokens_mask<span style="color:#00e0e0">=</span>True<span style="color:#fefefe">)</span> 

tokenized_datasets <span style="color:#00e0e0">=</span> dataset.map<span style="color:#fefefe">(</span>tokenize_function<span style="color:#fefefe">,</span> batched<span style="color:#00e0e0">=</span>True<span style="color:#fefefe">,</span> remove_columns<span style="color:#00e0e0">=</span>[<span style="color:#abe338">"text"</span>]<span style="color:#fefefe">)</span> 
print<span style="color:#fefefe">(</span><span style="color:#abe338">"Train dataset size:"</span><span style="color:#fefefe">,</span> len<span style="color:#fefefe">(</span>tokenized_datasets[<span style="color:#abe338">"train"</span>]<span style="color:#fefefe">)</span><span style="color:#fefefe">)</span> 
print<span style="color:#fefefe">(</span><span style="color:#abe338">"Validation dataset size:"</span><span style="color:#fefefe">,</span> len<span style="color:#fefefe">(</span>tokenized_datasets[<span style="color:#abe338">"validation"</span>]<span style="color:#fefefe">)</span><span style="color:#fefefe">)</span> 
data_collator <span style="color:#00e0e0">=</span> DataCollatorForLanguageModeling<span style="color:#fefefe">(</span>tokenizer<span style="color:#00e0e0">=</span>tokenizer<span style="color:#fefefe">,</span> mlm<span style="color:#00e0e0">=</span>False<span style="color:#fefefe">)</span> 
training_args <span style="color:#00e0e0">=</span> TrainingArguments<span style="color:#fefefe">(</span> 
    output_dir<span style="color:#00e0e0">=</span><span style="color:#abe338">"./results"</span><span style="color:#fefefe">,</span> 
    overwrite_output_dir<span style="color:#00e0e0">=</span>True<span style="color:#fefefe">,</span> 
    num_train_epochs<span style="color:#00e0e0">=</span><span style="color:#00e0e0">5</span><span style="color:#fefefe">,</span> 
    per_device_train_batch_size<span style="color:#00e0e0">=</span><span style="color:#00e0e0">64</span><span style="color:#fefefe">,</span> 
    save_steps<span style="color:#00e0e0">=</span>10_000<span style="color:#fefefe">,</span> 
    save_total_limit<span style="color:#00e0e0">=</span><span style="color:#00e0e0">2</span><span style="color:#fefefe">,</span> 
    remove_unused_columns<span style="color:#00e0e0">=</span>False<span style="color:#fefefe">,</span> 
    report_to<span style="color:#00e0e0">=</span>[]<span style="color:#fefefe">,</span> 
    learning_rate<span style="color:#00e0e0">=</span><span style="color:#00e0e0">5e-4</span> 
<span style="color:#fefefe">)</span> 
 
trainer <span style="color:#00e0e0">=</span> Trainer<span style="color:#fefefe">(</span> 
    model<span style="color:#00e0e0">=</span>model<span style="color:#fefefe">,</span> 
    args<span style="color:#00e0e0">=</span>training_args<span style="color:#fefefe">,</span> 
    data_collator<span style="color:#00e0e0">=</span>data_collator<span style="color:#fefefe">,</span> 
    train_dataset<span style="color:#00e0e0">=</span>tokenized_datasets[<span style="color:#abe338">"train"</span>]<span style="color:#fefefe">,</span> 
    eval_dataset<span style="color:#00e0e0">=</span>tokenized_datasets[<span style="color:#abe338">"validation"</span>] 
<span style="color:#fefefe">)</span> 
 
<span style="color:#00e0e0">if</span> torch.cuda.is_available<span style="color:#fefefe">(</span><span style="color:#fefefe">)</span><span style="color:#fefefe">:</span> 
    model.cuda<span style="color:#fefefe">(</span><span style="color:#fefefe">)</span> 

trainer.train<span style="color:#fefefe">(</span><span style="color:#fefefe">)</span> </code></span></span>

 

 

由于模型很小,因此可以使用单个 H100 GPU 进行预训练。

训练结果如下:

训练损失
5006.505700
10005.657100
15005.269900
20004.972000
25004.725000

 

训练好的模型可以用于推理验证。

 

 

<span style="background-color:#2b2b2b"><span style="color:#f8f8f2"><code class="language-applescript">model <span style="color:#00e0e0">=</span> GPT2LMHeadModel.from_pretrained<span style="color:#fefefe">(</span><span style="color:#abe338">"./results/checkpoint-2870"</span><span style="color:#fefefe">)</span> 
tokenizer <span style="color:#00e0e0">=</span> GPT2Tokenizer.from_pretrained<span style="color:#fefefe">(</span><span style="color:#abe338">"gpt2"</span><span style="color:#fefefe">)</span> 
tokenizer.pad_token <span style="color:#00e0e0">=</span> tokenizer.eos_token 
device <span style="color:#00e0e0">=</span> torch.device<span style="color:#fefefe">(</span><span style="color:#abe338">"cuda"</span> <span style="color:#00e0e0">if</span> torch.cuda.is_available<span style="color:#fefefe">(</span><span style="color:#fefefe">)</span> <span style="color:#00e0e0">else</span> <span style="color:#abe338">"cpu"</span><span style="color:#fefefe">)</span> 
model.<span style="color:#00e0e0">to</span><span style="color:#fefefe">(</span>device<span style="color:#fefefe">)</span> 
model.eval<span style="color:#fefefe">(</span><span style="color:#fefefe">)</span> 
input_text <span style="color:#00e0e0">=</span> <span style="color:#abe338">"Once upon a time"</span> 
inputs <span style="color:#00e0e0">=</span> tokenizer<span style="color:#fefefe">(</span>input_text<span style="color:#fefefe">,</span> return_tensors<span style="color:#00e0e0">=</span><span style="color:#abe338">"pt"</span><span style="color:#fefefe">,</span> padding<span style="color:#00e0e0">=</span>True<span style="color:#fefefe">)</span>.<span style="color:#00e0e0">to</span><span style="color:#fefefe">(</span>device<span style="color:#fefefe">)</span> 
<span style="color:#00e0e0">with</span> torch.no_grad<span style="color:#fefefe">(</span><span style="color:#fefefe">)</span><span style="color:#fefefe">:</span> 
    outputs <span style="color:#00e0e0">=</span> model.generate<span style="color:#fefefe">(</span> 
        inputs.input_ids<span style="color:#fefefe">,</span> 
        attention_mask<span style="color:#00e0e0">=</span>inputs.attention_mask<span style="color:#fefefe">,</span> 
        max_length<span style="color:#00e0e0">=</span><span style="color:#00e0e0">100</span><span style="color:#fefefe">,</span> 
        num_return_sequences<span style="color:#00e0e0">=</span><span style="color:#00e0e0">1</span><span style="color:#fefefe">,</span> 
        no_repeat_ngram_size<span style="color:#00e0e0">=</span><span style="color:#00e0e0">2</span><span style="color:#fefefe">,</span> 
        early_stopping<span style="color:#00e0e0">=</span>True<span style="color:#fefefe">,</span> 
        temperature<span style="color:#00e0e0">=</span><span style="color:#00e0e0">0.7</span><span style="color:#fefefe">,</span> 
        top_p<span style="color:#00e0e0">=</span><span style="color:#00e0e0">0.9</span><span style="color:#fefefe">,</span> 
        do_sample<span style="color:#00e0e0">=</span>True<span style="color:#fefefe">,</span> 
        pad_token_id<span style="color:#00e0e0">=</span>tokenizer.eos_token_id 
    <span style="color:#fefefe">)</span> 
generated_text <span style="color:#00e0e0">=</span> tokenizer.decode<span style="color:#fefefe">(</span>outputs[<span style="color:#00e0e0">0</span>]<span style="color:#fefefe">,</span> skip_special_tokens<span style="color:#00e0e0">=</span>True<span style="color:#fefefe">)</span> 
print<span style="color:#fefefe">(</span>generated_text<span style="color:#fefefe">)</span> </code></span></span>

 

 

 

推理结果如下:

大地震发生后,当地社区成立了新政府,新政府中以“最突出”的军政府开始运作。

 

微调代码演示

当我们对模型进行微调时,通常指的是监督微调(SFT)。SFT 可分为参数高效微调(PEFT)和完全微调。在 PEFT 实现中,LoRA、QLoRA 和 GA-LoRA 等方法非常流行。

首先我们来看一下如何加载模型进行完全微调。我们使用AutoModelForCausalLM.from_pretrained类来检索预训练模型的参数。

 

 

 

<span style="background-color:#2b2b2b"><span style="color:#f8f8f2"><code class="language-applescript"> model <span style="color:#00e0e0">=</span> AutoModelForCausalLM.from_pretrained<span style="color:#fefefe">(</span>
          model_name<span style="color:#fefefe">,</span> attn_implementation<span style="color:#00e0e0">=</span>attn_implementation<span style="color:#fefefe">,</span> device_map<span style="color:#00e0e0">=</span><span style="color:#fefefe">{</span><span style="color:#abe338">""</span><span style="color:#fefefe">:</span> <span style="color:#00e0e0">0</span><span style="color:#fefefe">}</span>
<span style="color:#fefefe">)</span>
model.gradient_checkpointing_enable<span style="color:#fefefe">(</span>gradient_checkpointing_kwargs<span style="color:#00e0e0">=</span><span style="color:#fefefe">{</span>'use_reentrant'<span style="color:#fefefe">:</span>True<span style="color:#fefefe">}</span><span style="color:#fefefe">)</span></code></span></span>

 

 

有关完整的微调代码,请参阅存储库:

https://github.com/davidsajare/david-share/tree/master/Deep-Learning/SmolLM-Full-Fine-Tuning

 

接下来我们看一下fine-tuning、LoRA、QLoRA在代码实现上的区别。在加载模型和训练参数方面,Full Fine-Tuning、LoRA、QLoRA有以下几点区别:

加载模型的差异

  • 全面微调
    • 直接加载完整模型进行训练。
    • 使用AutoModelForCausalLM.from_pretrained加载模型。
  • 洛拉
    • 加载模型,然后使用 LoRA 配置进行参数有效的微调。
    • 使用peft库中的LoraConfig来配置 LoRA 参数。
    • 目标模块通常是特定的投影层,例如k_proj 、q_proj等。
  • 量子LoRA
    • 它基于LoRA,结合量化技术(例如4位量化)以减少内存使用量。
    • 使用BitsAndBytesConfig进行量化配置。
    • 调用prepare_model_for_kbit_training准备模型。

训练参数的差异

  • 全面微调
    • 训练所有模型参数。
    • 通常需要更多的内存和计算资源。
    • 使用标准优化器,如adamw_torch 。
  • 洛拉
    • 仅训练LoRA插入的低秩矩阵,保持其他参数不变。
    • 训练速度更快,内存使用更少。
    • 使用像paged_adamw_8bit这样的优化器。
  • 量子LoRA
    • 结合LoRA和量化技术,进一步减少内存使用量。
    • 适用于在资源受限的环境中微调大型模型。
    • 还使用paged_adamw_8bit优化器。

需要注意的是,在进行LoRA或QLoRA微调时,我们可以指定需要训练的模块,例如:

 

 

<span style="background-color:#2b2b2b"><span style="color:#f8f8f2"><code class="language-applescript">model <span style="color:#00e0e0">=</span> FastLanguageModel.get_peft_model<span style="color:#fefefe">(</span>
    model<span style="color:#fefefe">,</span>
    r <span style="color:#00e0e0">=</span> <span style="color:#00e0e0">128</span><span style="color:#fefefe">,</span>
    target_modules <span style="color:#00e0e0">=</span> [<span style="color:#abe338">"q_proj"</span><span style="color:#fefefe">,</span> <span style="color:#abe338">"k_proj"</span><span style="color:#fefefe">,</span> <span style="color:#abe338">"v_proj"</span><span style="color:#fefefe">,</span> <span style="color:#abe338">"o_proj"</span><span style="color:#fefefe">,</span>
                      <span style="color:#abe338">"gate_proj"</span><span style="color:#fefefe">,</span> <span style="color:#abe338">"up_proj"</span><span style="color:#fefefe">,</span> <span style="color:#abe338">"down_proj"</span><span style="color:#fefefe">,</span>
                      <span style="color:#abe338">"embed_tokens"</span><span style="color:#fefefe">,</span> <span style="color:#abe338">"lm_head"</span><span style="color:#fefefe">,</span>]<span style="color:#fefefe">,</span> <span style="color:#d4d0ab"># Add for continual pretraining</span>
    lora_alpha <span style="color:#00e0e0">=</span> <span style="color:#00e0e0">32</span><span style="color:#fefefe">,</span>
    lora_dropout <span style="color:#00e0e0">=</span> <span style="color:#00e0e0">0</span><span style="color:#fefefe">,</span> <span style="color:#d4d0ab"># Supports any, but = 0 is optimized</span>
    bias <span style="color:#00e0e0">=</span> <span style="color:#abe338">"none"</span><span style="color:#fefefe">,</span>    <span style="color:#d4d0ab"># Supports any, but = "none" is optimized</span>
    use_gradient_checkpointing <span style="color:#00e0e0">=</span> <span style="color:#abe338">"unsloth"</span><span style="color:#fefefe">,</span> <span style="color:#d4d0ab"># True or "unsloth" for very long context</span>
    random_state <span style="color:#00e0e0">=</span> <span style="color:#00e0e0">3407</span><span style="color:#fefefe">,</span>
    use_rslora <span style="color:#00e0e0">=</span> True<span style="color:#fefefe">,</span>
<span style="color:#fefefe">)</span></code></span></span>

 

 

详细信息请参考:

https://github.com/davidsajare/david-share/tree/master/Deep-Learning/Continue-Pre-training

 

分布式训练实现

毫无疑问,大型语言模型的预训练需要多节点、多 GPU 的配置,这就需要分布式训练。目前底层的分布式预训练可以通过调用 NCCL 来实现,更上层的工具有 Megatron、DeepSpeed、HF 的加速库(目前支持 FSDP)等,这些工具可以有效实现 DP/PP/TP。

 

关于使用 Megatron 结合 DeepSpeed 进行预训练的详细信息,参考:

https://github.com/davidsajare/david-share/tree/master/Deep-Learning/Megatron%2BDeepspeed-Pretrain-G...

 

DeepSpeed

有关使用 DeepSpeed 实现 SFT 的示例,参考:

https://github.com/davidsajare/david-share/tree/master/Multimodal-Models/DeepSpeed-FT-Stable-Diffusi...

 

目前一些开源的微调工具如Axolotl也可以直接与DeepSpeed对接,示例参考:

https://github.com/davidsajare/david-share/tree/master/Deep-Learning/Fine-tuning-with-Axolotl

加速

当使用 FSDP 与加速时,可以结合其他并行策略来实现更高效的训练。

  • 数据并行(DP)
    • FSDP本身是一种数据并行策略,通过分片模型参数实现。
  • 流水线并行性 (PP)
  • 模型可以分为多个阶段,每个阶段在不同的设备上运行。这需要手动对模型进行划分并管理数据流。
  • 张量并行(TP)
    • 单层的计算分布在多个设备上。这需要修改模型的计算图。

结合这些策略通常需要对模型和训练脚本进行大量的定制和调整。accelerate提供了一些工具来简化这些过程,但具体实现可能需要结合其他 PyTorch 库(如torch.distributed )和自定义代码。

有关带有加速的FSDP 的示例,请参阅:

https://github.com/davidsajare/david-share/tree/master/Deep-Learning/Llama-3.1-70B-FSDP-Fine-Tuning

在大模型有监督训练(SFT)过程中,数据多样性指的是训练数据集中的样本具有丰富的变化和多样的特征。数据多样性是一种衡量训练数据集中多个样本之间差异性的度量。 数据多样性对于SFT的有效训练非常重要。它能够帮助模型学习到更有效的特征表示,从而提高模型的泛化能力和性能。 首先,数据多样性能够促使模型学习到更丰富的特征。如果训练数据集中的样本都非常相似,模型可能会过度拟合这些特定数据,而无法捕捉到更广泛的特征变化。相反,如果训练数据集中包含了各种不同类型和变化幅度的样本,模型将更有可能学习到更通用的特征表示。 其次,数据多样性可以帮助模型更好地应对未见过的数据。如果模型只在单一模式或特定领域的样本上进行训练,它可能无法处理在训练期间未曾见过的样本。通过训练数据集中引入多样性,模型将更具有鲁棒性和泛化能力,能够更好地适应新的、未知的数据。 最后,数据多样性有助于增强模型的鲁棒性。因为在真实应用中,输入数据的多样性变化往往是不可避免的。通过在训练过程中暴露模型于各种样本,模型可以更好地应对各种输入变化,提高模型的鲁棒性和稳定性。 总之,在大模型有监督训练中,数据多样性是指训练数据集中样本具有丰富的变化和多样的特征。它对于模型的特征学习、泛化能力和应对未知数据具有重要作用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值