Llama2多分类多输出任务微调及数据集制备技术咨询
多分类多输出场景下Llama2微调方案及代码示例
一、标签编码优化方案
针对Y1/Y2/Y3各含200+类别的稀疏性问题,不要将所有标签堆叠为单一稀疏向量,正确做法是对每个输出任务独立编码:
- 用
sklearn.preprocessing.LabelEncoder分别对Y1、Y2、Y3做类别映射,将每个类别转为唯一整数ID(例如Y1的类别A→0、B→1…,Y2的类别X→0、Y→1…) - 训练时为每个输出任务配置独立分类头,避免稀疏矩阵带来的内存浪费与计算低效
二、数据集制备模板
以下是适配该场景的预处理代码:
import numpy as np import pandas as pd from sklearn.preprocessing import LabelEncoder from transformers import LlamaTokenizer # 加载数据集(假设为CSV格式,包含x1,x2,x3,y1,y2,y3列) df = pd.read_csv("your_dataset.csv") # 1. 输入预处理:合并x1/x2/x3为文本输入并分词 tokenizer = LlamaTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf") tokenizer.pad_token = tokenizer.eos_token # Llama默认无pad token,需手动指定 def preprocess_input(row): input_text = f"x1: {row['x1']} x2: {row['x2']} x3: {row['x3']}" return tokenizer( input_text, truncation=True, max_length=512, padding="max_length", return_tensors="pt" ) # 批量处理输入 inputs = df.apply(preprocess_input, axis=1) input_ids = np.vstack([x["input_ids"].numpy() for x in inputs]) attention_mask = np.vstack([x["attention_mask"].numpy() for x in inputs]) # 2. 标签编码:独立处理每个输出任务 label_encoders = {} labels = {} for col in ["y1", "y2", "y3"]: le = LabelEncoder() labels[col] = le.fit_transform(df[col]) label_encoders[col] = le # 保存编码器,供后续推理时还原类别 import joblib joblib.dump(label_encoders, "label_encoders.pkl")
三、Llama2多输出微调代码
自定义多分类头部,针对三个输出任务分别计算交叉熵损失并求和:
import torch import torch.nn as nn from transformers import LlamaModel, TrainingArguments, Trainer from datasets import Dataset # 构建自定义多输出分类模型 class LlamaMultiOutputClassifier(nn.Module): def __init__(self, llama_model_name, num_classes_y1, num_classes_y2, num_classes_y3): super().__init__() self.llama = LlamaModel.from_pretrained(llama_model_name) self.dropout = nn.Dropout(0.1) # 为每个输出任务配置独立分类头 self.classifier_y1 = nn.Linear(self.llama.config.hidden_size, num_classes_y1) self.classifier_y2 = nn.Linear(self.llama.config.hidden_size, num_classes_y2) self.classifier_y3 = nn.Linear(self.llama.config.hidden_size, num_classes_y3) def forward(self, input_ids, attention_mask=None, labels=None): outputs = self.llama(input_ids=input_ids, attention_mask=attention_mask) pooled_output = outputs.last_hidden_state[:, 0, :] # 取<s> token的输出作为全局特征 pooled_output = self.dropout(pooled_output) logits_y1 = self.classifier_y1(pooled_output) logits_y2 = self.classifier_y2(pooled_output) logits_y3 = self.classifier_y3(pooled_output) loss = None if labels is not None: # 计算三个任务的交叉熵损失并求和 loss_fct = nn.CrossEntropyLoss() loss_y1 = loss_fct(logits_y1, labels["y1"]) loss_y2 = loss_fct(logits_y2, labels["y2"]) loss_y3 = loss_fct(logits_y3, labels["y3"]) loss = loss_y1 + loss_y2 + loss_y3 return {"loss": loss, "logits_y1": logits_y1, "logits_y2": logits_y2, "logits_y3": logits_y3} # 转换为HuggingFace Dataset格式 dataset_dict = { "input_ids": input_ids, "attention_mask": attention_mask, "y1": labels["y1"], "y2": labels["y2"], "y3": labels["y3"] } dataset = Dataset.from_dict(dataset_dict) dataset.set_format("torch", columns=["input_ids", "attention_mask", "y1", "y2", "y3"]) # 划分训练集与验证集 dataset = dataset.train_test_split(test_size=0.1) # 初始化模型 num_classes_y1 = len(label_encoders["y1"].classes_) num_classes_y2 = len(label_encoders["y2"].classes_) num_classes_y3 = len(label_encoders["y3"].classes_) model = LlamaMultiOutputClassifier( "meta-llama/Llama-2-7b-hf", num_classes_y1, num_classes_y2, num_classes_y3 ) # 定义训练参数 training_args = TrainingArguments( output_dir="./llama_multi_output_finetune", per_device_train_batch_size=4, per_device_eval_batch_size=4, learning_rate=2e-5, num_train_epochs=3, logging_dir="./logs", logging_steps=10, evaluation_strategy="epoch", save_strategy="epoch", fp16=True, # GPU支持时开启混合精度训练,节省显存 gradient_accumulation_steps=4 ) # 自定义验证集评估指标 def compute_metrics(eval_pred): logits_y1, logits_y2, logits_y3 = eval_pred.predictions labels_y1, labels_y2, labels_y3 = eval_pred.label_ids preds_y1 = np.argmax(logits_y1, axis=1) preds_y2 = np.argmax(logits_y2, axis=1) preds_y3 = np.argmax(logits_y3, axis=1) accuracy_y1 = (preds_y1 == labels_y1).mean() accuracy_y2 = (preds_y2 == labels_y2).mean() accuracy_y3 = (preds_y3 == labels_y3).mean() return { "accuracy_y1": accuracy_y1, "accuracy_y2": accuracy_y2, "accuracy_y3": accuracy_y3, "avg_accuracy": (accuracy_y1 + accuracy_y2 + accuracy_y3)/3 } # 初始化Trainer,自定义数据整理器 trainer = Trainer( model=model, args=training_args, train_dataset=dataset["train"], eval_dataset=dataset["test"], compute_metrics=compute_metrics, data_collator=lambda data: { "input_ids": torch.stack([x["input_ids"] for x in data]), "attention_mask": torch.stack([x["attention_mask"] for x in data]), "labels": { "y1": torch.tensor([x["y1"] for x in data]), "y2": torch.tensor([x["y2"] for x in data]), "y3": torch.tensor([x["y3"] for x in data]) } } ) # 启动训练 trainer.train() # 保存最终模型 trainer.save_model("./llama_multi_output_final")
四、关键注意事项
- Llama2的tokenizer需手动设置
pad_token(通常复用eos_token) - 若显存不足,可使用
bitsandbytes库进行4/8位量化,或减小per_device_train_batch_size、增大gradient_accumulation_steps - 可根据任务优先级调整损失权重(例如给核心输出任务设置更高的损失系数)
内容的提问来源于stack exchange,提问作者IndPythCoder
相关产品推荐
相关产品推荐

