使用 LlamaFactory 結合開源大語言模型實現文本分類:從數據集構建到 LoRA 微調與推理評估
背景介紹
本文將一步一步地,介紹如何使用llamafactory框架利用開源大語言模型完成文本分類的實驗,以 LoRA微調qwen/Qwen2.5-7B-Instruct為例。
文本分類數據集
按照 alpaca 樣式構建數據集,并在將其添加到LLaMA-Factory/data/dataset_info.json文件中。如此方便直接根據自定義數據集的名字,獲取到數據集的數據。
[
{
"instruction": "",
"input": "請將以下文本分類到一個最符合的類別中。以下是類別及其定義:\n\n要求}}\nreason: \nlabel:",
"output": "reason: 該文本主要討論的是xxx。因此,該文本最符合“社會管理”這一類別。\n\nlabel: 社會管理"
},
...
]
Lora 微調
llamafactory 框架支持網頁端訓練,但本文選擇在終端使用命令行微調模型。
模型微調訓練的參數較多,將模型訓練的參數都存儲在 yaml 文件中。
qwen_train_cls.yaml的文件內容如下:
### model
model_name_or_path: qwen/Qwen2.5-7B-Instruct
### method
stage: sft
do_train: true
finetuning_type: lora
lora_target: all
### dataset
# dataset_dir: data
dataset_dir: LLaMA-Factory/data/ 填寫相應路徑
dataset: 數據集名
template: qwen
cutoff_len: 2048
# max_samples: 1000 若數據集較大,可隨機篩選一部分數據微調模型
overwrite_cache: true
preprocessing_num_workers: 16
### output
output_dir: output/qwen2.5-7B/cls_epoch2 訓練的LoRA權重輸出路徑
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true
### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 1.0e-4
num_train_epochs: 2.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000
### eval
# val_size: 0.1
# per_device_eval_batch_size: 1
# eval_strategy: steps
# eval_steps: 500
使用下述命令啟動模型訓練:
nohup llamafactory-cli train qwen_train_cls.yaml > qwen_train_cls.log 2>&1 &
nohup 作用是讓命令在退出終端后仍然運行,防止因關閉終端或會話中斷導致進程被終止。
模型部署與推理
模型訓練完成后得到 Lora 權重。相關微調模型部署與推理,請瀏覽下述兩篇文章,相比llamafactory原本的模型推理速度更快。
- 基于 LLamafactory 的異步API高效調用實現與速度對比.https://blog.csdn.net/sjxgghg/article/details/144176645
- 基于 LlamaFactory 的 LoRA 微調模型支持 vllm 批量推理的實現
目前llamafactory已經支持 vllm_infer 推理,這個PR是筆者提交的:
- llamafactory vllm.https://github.com/hiyouga/LLaMA-Factory/blob/main/scripts/vllm_infer.py
期待模型的輸出結果
下述是使用 llamafactory 推理出的數據格式,建議大家在做推理評估時,也做成這個樣式,方便統一評估。
prompt 是大模型提示詞,predict 是大模型推理的結果,與真實的 label。
{
"prompt": "請將以下文本分類到一個最符合的類別中。以下是類別及其定義:...",
"predict": "\nreason: 該文本主要討論了改革創新發展、行政區劃調整、行政管理體制等方面的內容,涉及到體制機制的改革與完善,旨在推動高質量發展和提升生活品質。這些內容與社會管理和經濟管理密切相關,但更側重于行政管理和社會治理的改革,因此更符合“社會管理”這一類別。\n\nlabel: 社會管理",
"label": "reason: 該文本主要討論的是改革創新、行政區劃調整、體制機制障礙的破除以及行政管理體制等與政府治理和社會管理相關的內容,強調了與高質量發展和生活品質的關系。這些內容顯示出對社會管理和行政管理的關注,尤其是在推動城鄉一體化和適應高質量發展要求方面。因此,該文本最符合“社會管理”這一類別。\n\nlabel: 社會管理"
}
文本分類評估代碼
import os
import re
import json
from sklearn.metrics import classification_report, confusion_matrix
# 文本類別
CLASS_NAME = [
"產業相關",
...
"法律法規與行政事務",
"其他",
]
def load_jsonl(file_path):
"""
加載指定路徑的 JSON 文件并返回解析后的數據。
:param file_path: JSON 文件的路徑
:return: 解析后的數據(通常是字典或列表)
:raises FileNotFoundError: 如果文件未找到
:raises json.JSONDecodeError: 如果 JSON 格式不正確
"""
data = []
try:
with open(file_path, "r", encoding="utf-8") as file:
for line in file:
tmp = json.loads(line)
data.append(tmp)
except FileNotFoundError as e:
print(f"文件未找到:{file_path}")
raise e
except json.JSONDecodeError as e:
print(f"JSON 格式錯誤:{e}")
raise e
return data
def parser_label(text: str):
pattern = r"label[::\s\.\d\*]*([^\s^\*]+)"
matches = re.findall(pattern, text, re.DOTALL)
if len(matches) == 1:
return matches[0]
return None
def trans2num(item):
predict = parser_label(item["predict"])
label = parser_label(item["label"])
predict_idx = -1
label_idx = -1
for idx, cls_name in enumerate(CLASS_NAME):
if predict == cls_name:
predict_idx = idx
if label == cls_name:
label_idx = idx
return predict_idx, label_idx
def cls_eval(input_file):
data = load_jsonl(file_path=input_file)
predicts = []
labels = []
for item in data:
predict, label = trans2num(item)
if label == -1:
continue
predicts.append(predict)
labels.append(label)
return classification_report(predicts, labels, output_dict=False)
本文使用了大模型生成式預測文本類別,我沒有使用結構化輸出的方式,大家可以使用結構化的json格式輸出,這樣在提取大模型預測結果的時候會方便很多。
大家按照自己模型的輸出結果,修改parser_label函數,這個函數用于從大模型的輸出結果提取label。
cls_eval("xxx/generated_predictions.jsonl")
就會得到下述的輸出結果:
-1代表模型預測的類別不在給定的類別中。
贊
收藏
回復
分享
微博
QQ
微信
舉報

回復
相關推薦