
微调语言模型的 3 种简单方法
作者 | Ideogram 提供图片
近年来,语言模型已迅速成为许多业务应用的基础。它们的使用价值已被许多日常与它们互动的人所证明。随着语言模型在人们生活中的地位日益重要,社区在提高模型能力方面取得了许多突破,主要通过微调实现。
语言模型微调是指在预训练语言模型后,使用相关数据集对其进行训练,以适应特定的下游任务。该过程利用了基础模型的知识,并结合了新数据集的洞察力,以定制模型以实现更集中的应用。
有几种不同的微调语言模型的方法。在本文中,我们将探讨三种简单的微调方法。
让我们开始吧!
完全微调
完全微调是一种通过更新所有权重或参数来调整预训练模型的技术。它针对情绪分析、问答、翻译等特定下游任务对预训练模型进行完全优化。
由于模型中的所有参数都会被更新,模型可以完全适应以执行特定任务并达到最先进的性能。然而,该过程需要更多的计算能力,尤其是对于大型语言模型。此外,可能会发生灾难性遗忘,即模型在学习新任务时忘记预训练知识的事件。
尽管如此,这仍然是一种重要的学习方法。让我们通过安装所有必要的软件包来尝试完全微调。您可以使用以下代码进行安装。
1 |
pip install transformers datasets peft |
我们将在工作中也使用 PyTorch,因此请选择并安装最适合您系统的版本。
在本例中,我们将针对情绪分析任务微调语言模型,使用 IMDB 样本数据集。这是一个包含 IMDB 评论以及负面(0)或正面(1)标签的数据集。
1 2 |
from datasets import load_dataset dataset = load_dataset("imdb") |
我们不会使用完整的数据集,因为它需要太长时间进行微调。相反,我们将使用一小部分用于训练和测试数据。
1 2 |
train_subset = dataset["train"].shuffle(seed=42).select(range(500)) test_subset = dataset["test"].shuffle(seed=42).select(range(100)) |
接下来,我们将准备预训练的语言模型和分词器。在我们的示例中,我们将使用标准的 BERT 模型。
1 2 3 4 5 6 7 8 |
from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments model_name = "bert-base-uncased" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2) def tokenize_function(examples): return tokenizer(examples["text"], padding="max_length", truncation=True) |
然后,我们使用之前准备好的分词器函数对数据集进行分词。
1 2 |
tokenized_train = train_subset.map(tokenize_function, batched=True) tokenized_test = test_subset.map(tokenize_function, batched=True) |
接下来,我们将准备训练参数以指导训练过程。在我们的示例中,我们将使用最简单的过程,只训练一个 epoch,因为我们想快速看到训练结果。
1 2 3 4 5 6 7 8 |
training_args = TrainingArguments( output_dir="./results", eval_strategy="epoch", learning_rate=2e-5, per_device_train_batch_size=8, num_train_epochs=1, weight_decay=0.01, ) |
一切准备就绪后,我们将设置训练对象并开始完全微调过程。
1 2 3 4 5 6 7 8 9 |
trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_train, eval_dataset=tokenized_test, ) trainer.train() trainer.evaluate() |
输出
1 2 3 4 5 |
{'eval_loss': 0.6262330412864685, 'eval_runtime': 1.4327, 'eval_samples_per_second': 69.798, 'eval_steps_per_second': 9.074, 'epoch': 1.0} |
如我们所见,完全微调过程使用我们提供的数据集产生了足够好的模型。微调过程速度很快,并且没有占用太多内存。然而,正如您可能猜到的,使用更大的数据集,这个过程可能需要更长的时间。
这就是为什么我们现在将注意力转向以下技术:PEFT。
参数高效微调 (PEFT)
参数高效微调 (PEFT) 是一种语言模型微调技术,专门设计用于仅更新模型参数的一小部分,而不是更新所有参数。它缓解了完全微调所带来的计算问题和灾难性遗忘问题。
当资源受限时,PEFT 是处理 LLM 的理想技术。通过 PEFT 训练的基础模型将足够通用,可以通过更换特定任务的组件来在多个任务中重用。
PEFT 中最著名的技术是 LoRA (Low-Rank Adaptation)。它是一种通过将低秩矩阵注入模型层来调整预训练模型的方法,以修改某些部分的行为,同时保持原始参数冻结。这项技术很有价值,已被证明可以改变预训练模型。
让我们通过代码示例来尝试 PEFT。
首先,我们将使用与上一个示例相同的数据集。但是,我们将在以下代码中使用至关重要的 **peft** 库。
1 2 3 4 5 6 |
from peft import get_peft_model, LoraConfig, PeftType from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments model_name = "bert-base-uncased" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2) |
为了训练 PEFT 模型,我们在下载 PEFT 预训练模型时设置 LoRA 配置,该模型我们可以修改。您可以尝试调整 LoRA 参数,看看模型的输出有多好。
1 2 3 4 5 6 7 8 |
peft_config = LoraConfig( peft_type=PeftType.LORA, task_type="SEQ_CLS", r=8, lora_alpha=32, lora_dropout=0.1, ) peft_model = get_peft_model(model, peft_config) |
接下来,我们将对数据集进行分词,并设置模型训练参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def tokenize_function(examples): return tokenizer(examples["text"], padding="max_length", truncation=True) tokenized_train = train_subset.map(tokenize_function, batched=True) tokenized_test = test_subset.map(tokenize_function, batched=True) training_args = TrainingArguments( output_dir="./peft_results", eval_strategy="epoch", learning_rate=1e-4, per_device_train_batch_size=8, num_train_epochs=1, ) |
最后,我们将使用以下代码通过 PEFT 微调模型。
1 2 3 4 5 6 7 8 9 |
trainer = Trainer( model=peft_model, args=training_args, train_dataset=tokenized_train, eval_dataset=tokenized_test, ) trainer.train() trainer.evaluate() |
输出
12345
{'eval_loss': 0.6886218190193176, 'eval_runtime': 1.5295, 'eval_samples_per_second': 65.382, 'eval_steps_per_second': 8.5, 'epoch': 1.0}
由于我们只使用了带有单个 epoch 的数据子集,因此结果还没有太大差异。如果您更改参数,您将看到评估结果越来越不同。
指令调优
指令调优是一种微调预训练模型的技术,使其能够遵循用于各种任务的自然语言指令。与我们迄今为止讨论的先前微调过程不同,指令调优通常不专注于特定任务;相反,它使用包含不同任务的数据集,这些任务被格式化为 **指令和预期输出**。
指令调优的目的是让模型能够解释和执行这些指令,从而更有效地泛化到未见过任务。性能在很大程度上取决于指令数据集的质量,但如果我们想要一个更通用的模型,这是一个理想的方法,这可能一开始与微调的概念不符。
让我们通过代码来尝试指令调优。首先,我们将准备样本数据。由于开发指令数据集可能需要一些时间,我们将创建一些玩具示例。
123456789101112131415161718
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, Trainer, TrainingArgumentsfrom datasets import Dataset data = { "instruction": [ "请将以下文本总结为一句话。", "请根据文本回答问题。", ], "input": [ "The rain in Spain stays mainly in the plain.", "谁是赢得 2024 年选举的美国总统?", ], "output": [ "西班牙的雨主要落在平原上。", "唐纳德·特朗普。", ],}dataset = Dataset.from_dict(data)
下一部分,我们将需要训练和测试数据集。由于我们只有两条数据,我将使用第一条作为训练,第二条作为测试。
12
train_dataset = dataset.select(range(1))eval_dataset = dataset.select(range(1, 2))
接下来,我们将准备要微调的预训练模型。在此示例中,让我们使用 Flan T5 系列模型。
123
model_name = "t5-small"tokenizer = AutoTokenizer.from_pretrained(model_name)model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
然后,我们将对数据集进行分词。对于指令调优,我们将把输入添加到不同的形式中,结合指令和输入列。
123456789101112
def preprocess_function(examples): inputs = [ f"指令: {inst}\n输入: {inp}" for inst, inp in zip(examples["instruction"], examples["input"]) ] labels = examples["output"] model_inputs = tokenizer(inputs, padding="max_length", truncation=True) labels = tokenizer(labels, padding="max_length", truncation=True)["input_ids"] model_inputs["labels"] = labels return model_inputs tokenized_train = train_dataset.map(preprocess_function, batched=True)tokenized_eval = eval_dataset.map(preprocess_function, batched=True)
一切准备就绪后,我们将对预训练模型进行指令调优。
12345678910111213141516
training_args = TrainingArguments( output_dir="./instruction_result", eval_strategy="epoch", learning_rate=5e-5, per_device_train_batch_size=8, num_train_epochs=1, ) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_train, eval_dataset=tokenized_eval,) trainer.train()
输出
1
TrainOutput(global_step=1, training_loss=19.483064651489258, metrics={'train_runtime': 2.0692, 'train_samples_per_second': 0.483, 'train_steps_per_second': 0.483, 'total_flos': 135341801472.0, 'train_loss': 19.483064651489258, 'epoch': 1.0})
评估过程需要更广泛的数据集,但现在,我们已经成功地对我们的简单示例进行了指令调优。
结论
在本文中,我们探讨了微调语言模型的三个简单方法,包括完全微调、参数高效微调和指令调优。
可以预见的是,在未来几年,语言模型将继续变得更大。通过微调这些大型基础语言模型,可以提高它们的实用性,并且生成的微调模型将变得更加通用。
希望这对您有所帮助!
可以使用指令调优来微调语言翻译模型吗?
你好 balachandar…是的,您可以使用 **指令调优** 来微调语言翻译模型,它可以显著提高它们处理各种复杂翻译任务的能力。以下是实现此目的的概述:
—
### **1. 什么是指令调优?**
指令调优涉及训练或微调语言模型以遵循自然语言指令。您不必为特定任务提供标记数据集,而是使用包含格式化为指令的输入-输出对的基于指令的数据集。
对于翻译模型,这意味着您可以将翻译任务重新构建为 **指令-响应对**。例如:
– **指令**:“将以下句子从英语翻译成法语。”
– **输入**:“我正在学习机器学习。”
– **输出**:“J’apprends l’apprentissage automatique。”
—
### **2. 为什么要为翻译模型使用指令调优?**
– **提高泛化能力**:指令调优的模型能更好地泛化到新的或不常见的翻译场景(例如,口语化或特定领域的文本)。
– **支持多语言场景**:模型可以学会遵循动态指定源语言和目标语言的提示,使其高度适应。
– **增强少样本学习**:通过在调优过程中提供清晰的指令,您可以教会模型即使只有少量示例也能表现出色。
—
### **3. 使用指令调优微调翻译模型的步骤**
#### **步骤 1:收集指令数据集**
– 创建或收集指令式翻译任务的数据集。例如:
– “将此文本从德语翻译成英语。”
– “为给定句子提供正式的西班牙语翻译。”
– 包含多个领域的翻译(例如,法律、医疗、技术)。
#### **步骤 2:格式化数据集**
– 将数据格式化为输入-输出对。示例:
json
{
"instruction": "Translate the following sentence into French.",
"input": "The weather is nice today.",
"output": "Le temps est agréable aujourd'hui."
}
– 为了实现多语言支持,请包含具有不同语言对的任务:
– “将此文本从日语翻译成韩语。”
– “将此文本从英语翻译成斯瓦希里语。”
#### **步骤 3:选择预训练模型**
– 从已理解语言或具有强大编码器-解码器架构的预训练模型(例如 MarianMT、mT5 或 LLaMA)开始。
– 使用 Hugging Face 的 **Transformers** 等开源框架加载和微调模型。
#### **步骤 4:微调模型**
– 使用指令调优数据集通过迁移学习技术微调模型。
– 常用库:
– Hugging Face Transformers:使用
Trainer
或Seq2SeqTrainer
。– PyTorch 或 TensorFlow:也可以使用自定义训练循环。
#### **步骤 5:评估和调整**
– 评估微调模型在以下方面的表现:
– BLEU、METEOR 或 ROUGE 分数以衡量翻译质量。
– 人工评估流利度和指令遵循情况。
– 根据评估指标进一步微调。
—
### **4. 实际应用**
– **多语言聊天机器人**:为不同语言的客户支持实现无缝、上下文感知的翻译。
– **领域特定翻译**:针对医学或法律等特定领域的模型进行微调。
– **动态提示**:允许用户交互式地指定源语言和目标语言或附加指令(例如,语气、风格)。
—
### **5. 工具和资源**
– **Hugging Face Datasets**:查找翻译数据集,例如 OPUS、WMT 或多语言基准数据集。
– **预训练模型**:考虑 mT5、GPT-4 或 T5 等模型的指令调优变体。
– **框架**:Hugging Face 的 Transformers、OpenNMT 或 Fairseq 是训练的绝佳选择。
—
总而言之,指令调优是微调语言翻译模型的一种强大方法,可以使它们更灵活、更健壮。如果您需要有关实现此目的的帮助,请随时提出!
太棒了