引言

自然语言处理 (NLP) 领域的进展日新月异,你方唱罢我登场。因此,在实际场景中,针对特定的任务,我们经常需要对不同的语言模型进行比较,以寻找最适合的模型。本文主要比较 3 个模型: RoBERTa、Mistral-7B 及 Llama-2-7B。我们用它们来解决一个常见问题 —— 对灾难相关的推文进行分类。值得注意的是,Mistral 和 Llama 2 是 70 亿参数的大模型。相形之下,RoBERTa-large (355M 参数) 只是一个小模型,我们用它作为比较的基线。

本文,我们使用 PEFT (Parameter-Efficient Fine-Tuning,参数高效微调) 技术: LoRA (Low-Rank Adaptation,低秩适配) 来微调带序列分类任务头的预训练模型。LoRA 旨在显著减少可训参数量,同时保持强大的下游任务性能。

本文的主要目标是通过对 Hugging Face 的三个预训练模型进行 LoRA 微调,使之适用于序列分类任务。这三个预训练模型分别是: meta-llama/Llama-2-7b-hfmistralai/Mistral-7B-v0.1roberta-large

使用的硬件

  • 节点数: 1
  • 每个节点的 GPU 数: 1
  • GPU 类型: A6000
  • GPU 显存: 48GB

目标

  • 使用 LoRA PEFT 方法对预训练 LLM 进行微调。
  • 了解如何使用 Hugging Face 的各种 API (transformerspeft 以及 datasets)。
  • 使用 Weights & Biases 进行超参调优以及实验日志记录。

软件依赖

  1. datasets
  2. evaluate
  3. peft
  4. scikit-learn
  5. torch
  6. transformers
  7. wandb

注意: 要准确重现本文结果,请注意确保软件版本与 wandb 报告 的一致。

预训练模型

RoBERTa

RoBERTa (Robustly Optimized BERT Approach) 是 Meta AI 研究团队提出的改进版 BERT 模型。BERT 是一种基于 transformer 的语言模型,其基于自注意力机制对单词进行上下文感知的表征,并基于掩码语言模型目标进行训练。请注意,BERT 作为编码器模型,仅可用于自然语言理解任务 (例如序列分类和词元分类)。

RoBERTa 是一种流行的可微调模型,很适合作为我们实验的基线。欲了解更多信息,你可以查阅其 Hugging Face 模型卡

Llama 2

Llama 2 (Large Language Model Meta AI) 是 Meta AI 推出的一系列大语言模型 (LLM),其模型大小各异,参数量从 70 亿到 650 亿不等。

Llama 2 是一种基于 transformer 解码器架构的自回归语言模型。Llama 2 接受单词序列作为输入,并基于滑动窗口迭代预测下一个词元,从而实现文本生成的功能。

Llama 2 的架构与 GPT-3 等模型略有不同。举几个例子,Llama 2 采用 SwiGLU 激活函数而不是 ReLU,另外其位置嵌入使用的是旋转位置嵌入而不是可训绝对位置嵌入。

最近发布的 Llama 2 还对架构进行了改进,其将支持的最大上下文长度扩展到 4096 个词元,并使用分组查询注意 (grouped-query attention,GQA) 解码机制来更好地利用长序列。

Mistral 7B

Mistral 7B v0.1 有 73 亿个参数,是 Mistral AI 推出的第一个 LLM。

Mistral 7B 架构使用的新技术主要有:

  • 滑窗注意力: 用基于滑动窗口的注意力替换完整注意力 (平方级计算成本),其中每个词元最多可以关注上一层的 4096 个词元 (线性计算成本)。这样,多层以后,Mistral 7B 的实际关注词元数会叠加,因此更高层的注意力实际关注的总历史词元数会超过 4096。
  • 分组查询注意力: Llama 2 也使用了该技术,其通过缓存先前解码的词元的键向量和值向量来优化推理过程 (减少处理时间)。

LoRA

PEFT (Parameter Efficient Fine-Tuning,参数高效微调) 包含 p-tuning、前缀微调 (prefix-tuning) 、IA3、适配器微调以及 LoRA 等一系列技术,其旨在通过仅微调大模型的一个小参数集,就能达到全模型微调的性能水平。

LoRA (Low-Rank Adaptation,低阶适配) 的方法与添加适配层类似。其主要目标是减少模型的可训参数量。LoRA 的主要做法是冻结预训练权重,仅更新一个新增的低秩矩阵。

环境设置

RoBERTa 支持的最大序列长度为 512,为公平起见,对所有模型,我们统一设定 MAX_LEN=512

  1. MAX_LEN = 512
  2. roberta_checkpoint = "roberta-large"
  3. mistral_checkpoint = "mistralai/Mistral-7B-v0.1"
  4. llama_checkpoint = "meta-llama/Llama-2-7b-hf"

数据准备

数据加载

从 Hugging Face 加载数据集:

  1. from datasets import load_dataset
  2. dataset = load_dataset("mehdiiraqui/twitter_disaster")

将数据集分为训练集和验证集,同时加载测试集:

  1. from datasets import Dataset
  2. # 将数据集的训练集划分为训练集和验证集
  3. data = dataset['train'].train_test_split(train_size=0.8, seed=42)
  4. # 把划分而得的测试集重命名为验证集
  5. data['val'] = data.pop("test")
  6. # 将原数据集的测试集仍作为测试集
  7. data['test'] = dataset['test']

以下是数据集概览:

  1. DatasetDict({
  2. train: Dataset({
  3. features: ['id', 'keyword', 'location', 'text', 'target'],
  4. num_rows: 6090
  5. })
  6. val: Dataset({
  7. features: ['id', 'keyword', 'location', 'text', 'target'],
  8. num_rows: 1523
  9. })
  10. test: Dataset({
  11. features: ['id', 'keyword', 'location', 'text', 'target'],
  12. num_rows: 3263
  13. })
  14. })

首先,检查一下数据分布:

  1. import pandas as pd
  2. data['train'].to_pandas().info()
  3. data['test'].to_pandas().info()
  • 训练集
  1. RangeIndex: 7613 entries, 0 to 7612
  2. Data columns (total 5 columns):
  3. # Column Non-Null Count Dtype
  4. --- ------ -------------- -----
  5. 0 id 7613 non-null int64
  6. 1 keyword 7552 non-null object
  7. 2 location 5080 non-null object
  8. 3 text 7613 non-null object
  9. 4 target 7613 non-null int64
  10. dtypes: int64(2), object(3)
  11. memory usage: 297.5+ KB
  • 测试集
  1. <class 'pandas.core.frame.DataFrame'>
  2. RangeIndex: 3263 entries, 0 to 3262
  3. Data columns (total 5 columns):
  4. # Column Non-Null Count Dtype
  5. --- ------ -------------- -----
  6. 0 id 3263 non-null int64
  7. 1 keyword 3237 non-null object
  8. 2 location 2158 non-null object
  9. 3 text 3263 non-null object
  10. 4 target 3263 non-null int64
  11. dtypes: int64(2), object(3)
  12. memory usage: 127.6+ KB

训练集中标签分布情况:

  1. target
  2. 0 4342
  3. 1 3271
  4. Name: count, dtype: int64

由于类别不平衡,我们计算一下正负类权重,以用于稍后的损失计算:

  1. pos_weights = len(data['train'].to_pandas()) / (2 * data['train'].to_pandas().target.value_counts()[1])
  2. neg_weights = len(data['train'].to_pandas()) / (2 * data['train'].to_pandas().target.value_counts()[0])

计算出的权重为:

  1. POS_WEIGHT, NEG_WEIGHT = (1.1637114032405993, 0.8766697374481806)

接着,我们计算文本序列的最大长度:

  1. # 字符数
  2. max_char = data['train'].to_pandas()['text'].str.len().max()
  3. # 词数
  4. max_words = data['train'].to_pandas()['text'].str.split().str.len().max()
  1. The maximum number of characters is 152.
  2. The maximum number of words is 31.

数据处理

以一条训练数据为例:

  1. data['train'][0]
  1. {'id': 5285,
  2. 'keyword': 'fear',
  3. 'location': 'Thibodaux, LA',
  4. 'text': 'my worst fear. https://t.co/iH8UDz8mq3',
  5. 'target': 0}

该数据中包括关键字、位置和推文。为了简单起见,我们选择 text 特征作为 LLM 的唯一输入。

本阶段的目标是为 LLM 微调准备所需的 Hugging Face 格式的训练集、验证集和测试集。然后是定义用于训练的词元数据集,使用合适的分词器将 text 特征转换为词元 id 和注意力掩码序列这两个张量。由于每个模型都有其特定的分词器,因此我们需要生成三个不同的数据集,每个模型一个。

我们首先定义 RoBERTa 模型的数据加载器:

  • 加载与分词:
  1. from transformers import AutoTokenizer
  2. roberta_tokenizer = AutoTokenizer.from_pretrained(roberta_checkpoint, add_prefix_space=True)

注意: RoBERTa 分词器经过训练已将空格视为词元的一部分。因此,如果句子的第一个单词前面没有空格,则其编码会有所不同。为了确保第一个单词包含空格,我们设置 add_prefix_space=True 。同时,为了保持三个模型的预处理一致,我们将 Llama 2 和 Mistral 7B 的相应参数也设为 True

  • 定义每条数据的预处理函数:
  1. def roberta_preprocessing_function(examples):
  2. return roberta_tokenizer(examples['text'], truncation=True, max_length=MAX_LEN)

将预处理函数应用于训练数据集的第一条数据,我们得到了分词后的输入 ( input_ids ) 及其注意力掩码:

  1. roberta_preprocessing_function(data['train'][0])
  1. {'input_ids': [0, 127, 2373, 2490, 4, 1205, 640, 90, 4, 876, 73, 118, 725, 398, 13083, 329, 398, 119, 1343, 246, 2], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
  • 现在,将预处理函数应用于整个数据集:
  1. col_to_delete = ['id', 'keyword','location', 'text']
  2. # 删除不需要的列,并应用预处理函数
  3. roberta_tokenized_datasets = data.map(roberta_preprocessing_function, batched=True, remove_columns=col_to_delete)
  4. # 按照 HuggingFace 的要求,将 `target` 列 重命名为 `label` 列
  5. roberta_tokenized_datasets = roberta_tokenized_datasets.rename_column("target", "label")
  6. # 数据集格式设为 "torch"
  7. roberta_tokenized_datasets.set_format("torch")

注意: 我们从数据中删除了不需要的列: idkeywordlocationtext 。删除 text 的原因是我们已经将其转换为输入 id 和注意力掩码:

分词后的训练数据集中的数据如下:

  1. roberta_tokenized_datasets['train'][0]
  1. {'label': tensor(0),
  2. 'input_ids': tensor([ 0, 127, 2373, 2490, 4, 1205, 640, 90, 4, 876,
  3. 73, 118, 725, 398, 13083, 329, 398, 119, 1343, 246,
  4. 2]),
  5. 'attention_mask': tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])}
  • 为了生成训练 batch 数据,我们还需要对给定 batch 中的序列进行填充,以使 batch 中所有序列的长度都等于本 batch 最长序列的长度。为此,我们使用了 DataCollat​​orWithPadding 类:
  1. # 数据整理器将所有数据统一填充至 batch 内最长序列的长度
  2. from transformers import DataCollatorWithPadding
  3. roberta_data_collator = DataCollatorWithPadding(tokenizer=roberta_tokenizer)

用相同的流程为 Mistral 7B 和 Llama 2 模型准备数据:

注意 Llama 2 和 Mistral 7B 没有默认的 pad_token_id ,我们将其设为 eos_token_id

  • Mistral 7B:
  1. # 加载 Mistral 7B 分词器
  2. from transformers import AutoTokenizer, DataCollatorWithPadding
  3. mistral_tokenizer = AutoTokenizer.from_pretrained(mistral_checkpoint, add_prefix_space=True)
  4. mistral_tokenizer.pad_token_id = mistral_tokenizer.eos_token_id
  5. mistral_tokenizer.pad_token = mistral_tokenizer.eos_token
  6. def mistral_preprocessing_function(examples):
  7. return mistral_tokenizer(examples['text'], truncation=True, max_length=MAX_LEN)
  8. mistral_tokenized_datasets = data.map(mistral_preprocessing_function, batched=True, remove_columns=col_to_delete)
  9. mistral_tokenized_datasets = mistral_tokenized_datasets.rename_column("target", "label")
  10. mistral_tokenized_datasets.set_format("torch")
  11. # 序列填充
  12. mistral_data_collator = DataCollatorWithPadding(tokenizer=mistral_tokenizer)
  • Llama 2:
  1. # 加载 Llama 2 分词器
  2. from transformers import AutoTokenizer, DataCollatorWithPadding
  3. llama_tokenizer = AutoTokenizer.from_pretrained(llama_checkpoint, add_prefix_space=True)
  4. llama_tokenizer.pad_token_id = llama_tokenizer.eos_token_id
  5. llama_tokenizer.pad_token = llama_tokenizer.eos_token
  6. def llama_preprocessing_function(examples):
  7. return llama_tokenizer(examples['text'], truncation=True, max_length=MAX_LEN)
  8. llama_tokenized_datasets = data.map(llama_preprocessing_function, batched=True, remove_columns=col_to_delete)
  9. llama_tokenized_datasets = llama_tokenized_datasets.rename_column("target", "label")
  10. llama_tokenized_datasets.set_format("torch")
  11. # 序列填充
  12. llama_data_collator = DataCollatorWithPadding(tokenizer=llama_tokenizer)

至此,我们已经准备好了分词后的数据集,下一节我们将讨论如何加载预训练 LLM 检查点以及如何设置 LoRA 权重。

模型

RoBERTa

为分类任务加载 RoBERTa 检查点

我们使用 Hugging Face AutoModelForSequenceClassification 类加载带有序列分类头的预训练 RoBERTa 模型:

  1. from transformers import AutoModelForSequenceClassification
  2. roberta_model = AutoModelForSequenceClassification.from_pretrained(roberta_checkpoint, num_labels=2)

RoBERTa 分类器的 LoRA 设置

我们为 RoBERTa 分类器设置 LoRA 参数:

  • TaskType: 序列分类
  • r(rank): 分解矩阵的秩
  • lora_alpha: 用于对习得权重进行缩放的 alpha 参数。LoRA 论文建议将 alpha 固定为 16
  • lora_dropout: LoRA 层的 Dropout 概率
  • bias: 是否向 LoRA 层添加偏置

以下代码使用了 LoRA 论文 的推荐设置。后文 我们还将用 wandb 对这些超参进行调优。

  1. from peft import get_peft_model, LoraConfig, TaskType
  2. roberta_peft_config = LoraConfig(
  3. task_type=TaskType.SEQ_CLS, r=2, lora_alpha=16, lora_dropout=0.1, bias="none",
  4. )
  5. roberta_model = get_peft_model(roberta_model, roberta_peft_config)
  6. roberta_model.print_trainable_parameters()

可以看到,可训参数量仅占 RoBERTa 模型参数量的 0.64%:

  1. trainable params: 2,299,908 || all params: 356,610,052 || trainable%: 0.6449363911929212

Mistral

为分类任务加载检查点

加载带有序列分类头的预训练 Mistral-7B 模型:

  1. from transformers import AutoModelForSequenceClassification
  2. import torch
  3. mistral_model = AutoModelForSequenceClassification.from_pretrained(
  4. pretrained_model_name_or_path=mistral_checkpoint,
  5. num_labels=2,
  6. device_map="auto"
  7. )

设置填充词元 id,因为 Mistral 7B 没有默认填充词元。

  1. mistral_model.config.pad_token_id = mistral_model.config.eos_token_id

Mistral 7B 分类器的 LoRA 设置

对 Mistral 7B 模型而言,我们需要指定 target_modules (我们将其指定为注意力模块的查询向量映射层和值向量映射层):

  1. from peft import get_peft_model, LoraConfig, TaskType
  2. mistral_peft_config = LoraConfig(
  3. task_type=TaskType.SEQ_CLS, r=2, lora_alpha=16, lora_dropout=0.1, bias="none",
  4. target_modules=[
  5. "q_proj",
  6. "v_proj",
  7. ],
  8. )
  9. mistral_model = get_peft_model(mistral_model, mistral_peft_config)
  10. mistral_model.print_trainable_parameters()

可训参数量仅占 Mistral 模型参数量的 0.024%:

  1. trainable params: 1,720,320 || all params: 7,112,380,416 || trainable%: 0.02418768259540745

Llama 2

为分类任务加载检查点

加载带有序列分类头的预训练 Llama 2 模型。

  1. from transformers import AutoModelForSequenceClassification
  2. import torch
  3. llama_model = AutoModelForSequenceClassification.from_pretrained(
  4. pretrained_model_name_or_path=llama_checkpoint,
  5. num_labels=2,
  6. device_map="auto",
  7. offload_folder="offload",
  8. trust_remote_code=True
  9. )

设置填充词元 id,因为 Llama 2 没有默认填充词元。

  1. llama_model.config.pad_token_id = llama_model.config.eos_token_id

Llama 2 分类器的 LoRA 设置

使用与 Mistral 相同的 LoRA 参数:

  1. from peft import get_peft_model, LoraConfig, TaskType
  2. llama_peft_config = LoraConfig(
  3. task_type=TaskType.SEQ_CLS, r=16, lora_alpha=16, lora_dropout=0.05, bias="none",
  4. target_modules=[
  5. "q_proj",
  6. "v_proj",
  7. ],
  8. )
  9. llama_model = get_peft_model(llama_model, llama_peft_config)
  10. llama_model.print_trainable_parameters()

可训参数量仅占 Llama 2 模型参数量的 0.12%:

  1. trainable params: 8,404,992 || all params: 6,615,748,608 || trainable%: 0.1270452143516515

至此,我们定义了用于训练的词元数据集及 LoRA 设置。下面,我们介绍如何使用 Hugging Face 的 Trainer 类启动训练。

设置 Trainer

评估指标

首先,我们定义用于对三个模型的性能进行比较的指标: F1 分数、召回率、精确度和准确度:

  1. import evaluate
  2. import numpy as np
  3. def compute_metrics(eval_pred):
  4. # HF `evaluate` 包已支持我们所要的所有指标
  5. precision_metric = evaluate.load("precision")
  6. recall_metric = evaluate.load("recall")
  7. f1_metric= evaluate.load("f1")
  8. accuracy_metric = evaluate.load("accuracy")
  9. logits, labels = eval_pred
  10. # eval_pred 是模型返回的预测值和实际值元组
  11. predictions = np.argmax(logits, axis=-1)
  12. precision = precision_metric.compute(predictions=predictions, references=labels)["precision"]
  13. recall = recall_metric.compute(predictions=predictions, references=labels)["recall"]
  14. f1 = f1_metric.compute(predictions=predictions, references=labels)["f1"]
  15. accuracy = accuracy_metric.compute(predictions=predictions, references=labels)["accuracy"]
  16. # `Trainer` 要求将指标组织为一个字典,其键为指标名,值为分数。
  17. return {"precision": precision, "recall": recall, "f1-score": f1, 'accuracy': accuracy}

基于加权损失的自定义 Trainer

前文提到,数据集正负类分布并不平衡。因此,我们用加权交叉熵损失来训练模型以解决这个问题。 Trainer 类本身的实现中不支持自定义损失,因为它期望直接从模型的输出中获取损失。

因此,我们需要定义一个自定义的 WeightedCELossTrainer ,以重写 compute_loss 方法,该方法可以根据模型的预测和标签计算加权交叉熵损失:

  1. from transformers import Trainer
  2. class WeightedCELossTrainer(Trainer):
  3. def compute_loss(self, model, inputs, return_outputs=False):
  4. labels = inputs.pop("labels")
  5. # Get model's predictions
  6. outputs = model(**inputs)
  7. logits = outputs.get("logits")
  8. # Compute custom loss
  9. loss_fct = torch.nn.CrossEntropyLoss(weight=torch.tensor([neg_weights, pos_weights], device=model.device, dtype=logits.dtype))
  10. loss = loss_fct(logits.view(-1, self.model.config.num_labels), labels.view(-1))
  11. return (loss, outputs) if return_outputs else loss

Trainer 设置

我们为三个模型分别设置训练超参及训练器。

RoBERTa

第一步,把模型搬到 GPU 设备上。

  1. roberta_model = roberta_model.cuda()
  2. roberta_model.device()

It will print the following:

  1. device(type='cuda', index=0)

然后,设置训练超参:

  1. from transformers import TrainingArguments
  2. lr = 1e-4
  3. batch_size = 8
  4. num_epochs = 5
  5. training_args = TrainingArguments(
  6. output_dir="roberta-large-lora-token-classification",
  7. learning_rate=lr,
  8. lr_scheduler_type= "constant",
  9. warmup_ratio= 0.1,
  10. max_grad_norm= 0.3,
  11. per_device_train_batch_size=batch_size,
  12. per_device_eval_batch_size=batch_size,
  13. num_train_epochs=num_epochs,
  14. weight_decay=0.001,
  15. evaluation_strategy="epoch",
  16. save_strategy="epoch",
  17. load_best_model_at_end=True,
  18. report_to="wandb",
  19. fp16=False,
  20. gradient_checkpointing=True,
  21. )

最后,我们将模型、训练超参和词元数据集一起作为参数来实例化一个 RoBERTa 训练器:

  1. roberta_trainer = WeightedCELossTrainer(
  2. model=roberta_model,
  3. args=training_args,
  4. train_dataset=roberta_tokenized_datasets['train'],
  5. eval_dataset=roberta_tokenized_datasets["val"],
  6. data_collator=roberta_data_collator,
  7. compute_metrics=compute_metrics
  8. )

Mistral-7B

与 RoBERTa 类似,我们用如下代码初始化 WeightedCELossTrainer :

  1. from transformers import TrainingArguments, Trainer
  2. mistral_model = mistral_model.cuda()
  3. lr = 1e-4
  4. batch_size = 8
  5. num_epochs = 5
  6. training_args = TrainingArguments(
  7. output_dir="mistral-lora-token-classification",
  8. learning_rate=lr,
  9. lr_scheduler_type= "constant",
  10. warmup_ratio= 0.1,
  11. max_grad_norm= 0.3,
  12. per_device_train_batch_size=batch_size,
  13. per_device_eval_batch_size=batch_size,
  14. num_train_epochs=num_epochs,
  15. weight_decay=0.001,
  16. evaluation_strategy="epoch",
  17. save_strategy="epoch",
  18. load_best_model_at_end=True,
  19. report_to="wandb",
  20. fp16=True,
  21. gradient_checkpointing=True,
  22. )
  23. mistral_trainer = WeightedCELossTrainer(
  24. model=mistral_model,
  25. args=training_args,
  26. train_dataset=mistral_tokenized_datasets['train'],
  27. eval_dataset=mistral_tokenized_datasets["val"],
  28. data_collator=mistral_data_collator,
  29. compute_metrics=compute_metrics
  30. )

注意,我们需要将 fp16 设为 True 以启用半精度训练。主要原因是 Mistral-7B 很大,如果使用 fp32 精度,其权重无法放进单块 GPU 的显存 (48GB) 中。

Llama 2

与 Mistral 7B 类似,我们用如下代码定义训练器:

  1. from transformers import TrainingArguments, Trainer
  2. llama_model = llama_model.cuda()
  3. lr = 1e-4
  4. batch_size = 8
  5. num_epochs = 5
  6. training_args = TrainingArguments(
  7. output_dir="llama-lora-token-classification",
  8. learning_rate=lr,
  9. lr_scheduler_type= "constant",
  10. warmup_ratio= 0.1,
  11. max_grad_norm= 0.3,
  12. per_device_train_batch_size=batch_size,
  13. per_device_eval_batch_size=batch_size,
  14. num_train_epochs=num_epochs,
  15. weight_decay=0.001,
  16. evaluation_strategy="epoch",
  17. save_strategy="epoch",
  18. load_best_model_at_end=True,
  19. report_to="wandb",
  20. fp16=True,
  21. gradient_checkpointing=True,
  22. )
  23. llama_trainer = WeightedCELossTrainer(
  24. model=llama_model,
  25. args=training_args,
  26. train_dataset=llama_tokenized_datasets['train'],
  27. eval_dataset=llama_tokenized_datasets["val"],
  28. data_collator=llama_data_collator,
  29. compute_metrics=compute_metrics
  30. )

超参调优

我们用 Wandb Sweep API 通过贝叶斯搜索策略来进行超参调优 (30 次运行),待调优的超参搜索空间如下:

方法 指标 lora_alpha lora_bias lora_dropout lora_rank lr max_length
bayes 目标: maximize 分布: categorical 分布: categorical 分布: uniform 分布: categorical 分布: uniform 分布: categorical
目标名: eval/f1-score 取值集合:
-16
-32
-64
取值集合: None -最大值: 0.1
-最小值: 0
取值集合:
-4
-8
-16
-32
-最大值: 2e-04
-最小值: 1e-05
取值集合: 512

欲了解更多信息,可以查看 资源 一节中的 Wandb 实验报告。

结果

模型 F1 分数 训练时间 内存消耗 可训参数量
RoBERTa 0.8077 538 秒 GPU1: 9.1 GB
GPU2: 8.3 GB
0.64%
Mistral 7B 0.7364 2030 秒 GPU1: 29.6 Gb
GPU2: 29.5 GB
0.024%
Llama 2 0.7638 2052 秒 GPU1: 35 GB
GPU2: 33.9 GB
0.12%

总结

本文我们用 LoRA 对三个大语言模型 (LLM) (RoBERTa、Mistral 7B 及 Llama 2) 针对灾难推文分类任务进行微调。从性能结果来看,RoBERTa 的性能大幅优于 Mistral 7B 和 Llama 2。这就提出了一个问题: 我们是否真的需要一个大而复杂的 LLM 来完成诸如短序列二分类这样的简单任务?

一个重要的启示是,在选择要使用的 LLM 模型时应该考虑具体的项目要求、可用资源和性能需求。

此外,对于针对短序列的相对 简单 的预测任务,小的基础模型 (例如 RoBERTa) 仍然具有竞争力。

最后,我们还通过例子展示了 LoRA 方法的通用性,其既可应用于编码器 (RoBERTa) 模型,还可应用于解码器 (Llama 2 及 Mistral 7B) 模型。

资源

  1. 本文代码均已在该 Github 项目
  2. 下面是各模型的 Wandb 超参调优实验报告:

英文原文: https://hf.co/blog/Lora-for-sequence-classification-with-Roberta-Llama-Mistral

原文作者: Mehdi Iraqi

译者: Matrix Yao (姚伟峰),英特尔深度学习工程师,工作方向为 transformer-family 模型在各模态数据上的应用及大规模模型的训练推理。

在灾难推文分析场景上比较用 LoRA 微调 Roberta、Llama 2 和 Mistral 的过程及表现的更多相关文章

  1. AIOps背景/所应具备技术能力分析(上)

    本文篇幅较长,分为上,中,下,三个部分进行连载.内容分别为:AIOps 背景/所应具备技术能力分析(上),AIOps 常见的误解(中),挑战及建议(下). 前言 我大概是 5,6 年前开始接触 ITO ...

  2. 推文《阿里凑单算法首次公开!基于Graph Embedding的打包购商品挖掘系统解析》笔记

    推文<阿里凑单算法首次公开!基于Graph Embedding的打包购商品挖掘系统解析>笔记 从17年5月份开始接触Graph Embedding,学术论文读了很多,但是一直不清楚这技术是 ...

  3. LR12.53—第7课:分析场景

    第7课:分析场景 在前面的课程中,您学习如何设计,控制和执行方案运行.一旦您已加载您的服务器,你要分析的运行,并确定需要被淘汰,以提高系统性能的问题. 在图表和报告中有关方案的性能您的分析会议上提出的 ...

  4. 深度解析Java8 – AbstractQueuedSynchronizer的实现分析(上)

    本文首发在infoQ :www.infoq.com/cn/articles/jdk1.8-abstractqueuedsynchronizer 前言: Java中的FutureTask作为可异步执行任 ...

  5. BigQuery分析GitHub上的C#

    BigQuery分析GitHub上的C# 一年多以前,Google 在GitHub中提供了BigQuery用于查询的GitHub上的开源代码(open source code on GitHub av ...

  6. ISP算法高水平分析(上)

    ISP算法高水平分析(上) 一.ISP基本框架及算法介绍 ISP是Image Signal Processor的缩写,全称是影像处理器.在相机成像的整个环节中,它负责接收感光元件(Sensor)的原始 ...

  7. Linux内存技术分析(上)

    Linux内存技术分析(上) 一.Linux存储器 限于存储介质的存取速率和成本,现代计算机的存储结构呈现为金字塔型.越往塔顶,存取效率越高.但成本也越高,所以容量也就越小.得益于程序访问的局部性原理 ...

  8. 国内操作系统OS分析(上)

    国内操作系统OS分析(上) 一.操作系统(OS)概述 操作系统(OS,Operating System),是管理.控制计算机软硬件资源的计算机程序,并为用户提供一个与系统交互的操作界面.OS是配置在计 ...

  9. OpenMP 线程同步 Construct 实现原理以及源码分析(上)

    OpenMP 线程同步 Construct 实现原理以及源码分析(上) 前言 在本篇文章当中主要给大家介绍在 OpenMP 当中使用的一些同步的 construct 的实现原理,如 master, s ...

  10. “Word自动更改后的内容保存到通用文档模板上。是否加载该模板?“的解决办法

    在win7系统下,Word2010出现了不能正常关闭.打开一个已有word文档,点击右上角关闭按钮后,先提示"word已停止工作,windows正在检查该问题的解决方案",随后提示 ...

随机推荐

  1. Prompt 指北:如何写好 Prompt,让 GPT 的回答更加精准

    目录 1. 得亏 GPT 脾气好 2. 玩 GPT 得注意姿势 3. 指南指北指东指西 3.1 首先你得理解 GPT 是咋工作的 3.2 "Prompt 工程"走起 3.3 奇淫技 ...

  2. 我找回了我喜欢的Github Old Feed

    前言 这周Github更新了个人主页Feed(指的是用户的活动源或动态源),作为GitHub重度爱好者而言New Feed完全不是我之前所喜欢的效果.虽然说New Feed添加了允许用户可以自定义配置 ...

  3. Unity 游戏开发、03 基础篇 | C#初级编程

    C#初级编程 https://learn.u3d.cn/tutorial/beginner-gameplay-scripting 8 Update 和 FixedUpdate Update(不是按固定 ...

  4. Java并发Map的面试指南:线程安全数据结构的奥秘

    简介 在计算机软件开发的世界里,多线程编程是一个重要且令人兴奋的领域.然而,与其引人入胜的潜力相伴而来的是复杂性和挑战,其中之一就是处理共享数据.当多个线程同时访问和修改共享数据时,很容易出现各种问题 ...

  5. 用Rust手把手编写一个Proxy(代理), UDP绑定篇

    用Rust手把手编写一个Proxy(代理), UDP绑定篇 项目 ++wmproxy++ gite: https://gitee.com/tickbh/wmproxy github: https:// ...

  6. pyinstall打包相对路径问题

    pyinstall打包相对路径问题 ​ pyinstaller 打包py文件成exe文件,在没有python的机器上运行,执行打包后的程序,经常会出现程序使用的图标无法显示,程序使用的关联文件无法关联 ...

  7. pbjs 无法编码 bytes 类型数据问题的解决方案

    问题背景 之前写过一篇<使用脚本收发 protobuf 协议数据>,通过 pbjs 命令可以将 protobuf 二进制数据转换为 json: > pbjs msg.proto -- ...

  8. Flask框架——模板、数据库ORM

    文章目录 1 模板 1 重定向 1.1 什么是重定向? 1.2 为什么要有重定向? 1.3 如何使用重定向? 1.3.1 暂时性重定向(代码实例): 1.3.2 永久性重定向(代码实例) 2 jinj ...

  9. StackOverflow 并不只是一个问答网站

    首页    新文章  联系  管理  订阅  StackOverflow 并不只是一个问答网站 今天看到了一个网站的模仿StackOverflow的问答应用,有点儿感慨:是不是设计这个模仿应用的人,真 ...

  10. umicv cv-summary1-全连接神经网络模块化实现

    全连接神经网络模块化实现 Linear与Relu单层实现 LossLayer实现 多层神经网络 不同梯度下降方法 Dropout层 今天这篇博文针对Assignment3的全连接网络作业,对前面学习的 ...