大模型对话风格微调项目实战——模型篇

目录

背景介绍

本文是《大模型对话风格微调项目实战------数据工程篇》的后续文章,介绍了在该项目中如何进行模型的选择、评测、训练和效果评估。

这篇文章的任务

  1. 模型选型 :选择合适的大模型,如GPT-3、BERT等,以及合适的任务,如文本生成、对话生成等。
  2. 模型评测 :选择合适的评测指标,如BLEU、ROUGE等,以及合适的评测数据集,如WMT、MTBench等。
  3. 模型训练 :对选择的模型进行训练,训练方法使用QLoRA微调,训练数据集使用《数据工程篇》中生成的数据集。
  4. 模型训练效果评估 :对训练好的模型进行效果评估,评估方法使用OpenCompass评测框架。然后再进行主观评估。

模型选型

大模型选型原则

核心原则

  1. 选择适合任务的模型: 根据任务的需求,选择合适的模型。例如,对于对话生成任务,选择适合的对话模型;对于数学推理任务,选择适合的数学模型。
  2. 选择合适的语言模型: 根据任务的需求,选择合适的语言模型。例如,对于中文任务,选择中文语言模型;对于英文任务,选择英文语言模型。
  3. 选择合适的计算资源: 根据计算资源的限制,选择合适的模型。例如,对于小批量任务,可以选择较小的模型;对于大规模任务,可以选择较大的模型。

大模型选型

  1. 任务需求分析

    • 对话风格微调任务
      • 任务类型: 对话风格迁移,保持对话内容的不变性,同时调整对话风格为"温柔"或者"毒舌"。
      • 语言要求: 中文
    • 训练和推理资源限制
      • 目前可用的训练和推理资源有限
      • 训练显存: 24GB
      • 推理显存: 16GB
  2. 模型选择: 基于任务需求和资源限制,选择如下候选模型

    • qwen2.5-1.5B-Instruct(千问系列)
      • 核心能力:多语言对话与结构化输出
      • 参数规模:15亿参数
      • 突出特性
        • 支持29种语言跨语言交互
        • 处理32K上下文并生成8K token对话
        • 擅长编程对话和数学推理场景
        • 稳定生成JSON等结构化响应
        • 系统提示适应性强的角色扮演
    • internlm2_5-1_8b-chat(书生系列)
      • 核心能力:工具调用与复杂任务处理
      • 参数规模:18亿参数
      • 突出特性
        • 支持多轮工具链式调用
        • 自主完成网页信息搜集与分析
        • MATH评测得分40.2(同量级领先)
        • 增强的指令理解与结果反思
        • 适合多步骤推理的智能体构建

模型评测

评测框架

OpenCompass是一个开放的对话模型评估框架,用于评估对话模型的表现。OpenCompass提供了一系列的对话模型评估指标,包括BLEU、ROUGE、BERTScore等。

评测数据集

根据任务需求,选择CLUE(中文理解)数据集进行模型评测。

  1. FewCLUE_bustm_gen 短文本分类数据集

    • 任务类型:中文自然语言推理(NLI)任务,属于句对分类任务范畴
    • 数据构成:包含160条标注样本及约2万条未标注数据,通过多模板Prompt增强小样本学习效果
    • 技术特征:采用对抗训练、对比学习和Mixup数据增强策略,专门测试模型在挑战性推理对上的表现
    • 应用目标:评估预训练模型在少量样本下的逻辑推理能力及泛化性能
  2. FewCLUE_ocnli_fc_gen 中文自然语言推理数据集

    • 任务类型:中文短文本匹配任务,属于二分类句对判断任务
    • 数据构成:遵循统一标准配置,包含等量的标注样本(160条)与未标注数据(约2万条)
    • 技术特征:应用相同的多模板Prompt框架,结合对抗训练提升模型鲁棒性
    • 应用目标:验证模型在极低资源场景下的语义匹配能力与迁移学习效果

进行评测

安装OpenCompass评测框架

使用以下命令安装OpenCompass:

bash 复制代码
conda create -n opencompass python=3.10 -y
conda activate opencompass
git clone -c http.proxy=http://127.0.0.1:7890 https://github.com/open-compass/opencompass opencompass
cd opencompass
pip install -e .

下载模型

使用以下命令下载模型:

bash 复制代码
# 下载qwen2.5-1.5b-instruct模型
modelscope download --model Qwen/Qwen2.5-1.5B-Instruct --cache_dir /mnt/model

# 下载internlm2_5-1_8b-chat模型
modelscope download --model Shanghai_AI_Laboratory/internlm2_5-1_8b-chat --cache_dir /mnt/model

运行评测

使用以下命令运行评测:

bash 复制代码
python run.py \
    --models hf_qwen2_5_1_5b_instruct hf_internlm2_5_1_8b_chat \
    --datasets fewclue_bustm_gen fewclue_ocnli_fc_gen \
    --debug

# 推理框架加速模型评估(可选)
python run.py \
    --models lmdeploy_qwen2_5_1_5b_instruct lmdeploy_internlm2_5_1_8b_chat \
    --datasets fewclue_bustm_gen fewclue_ocnli_fc_gen \
    --debug

评测结果

评测结果如下:

dataset version metric mode qwen2.5-1.5b-instruct-hf internlm2_5-1_8b-chat-hf
ocnli_fc-dev 51e956 accuracy gen 63.12 58.13
ocnli_fc-test 51e956 accuracy gen 60.60 58.06

从评测结果可以看出,qwen2.5-1.5b-instruct模型在FewCLUE_ocnli_fc_gen数据集上的表现比internlm2_5-1_8b-chat模型好。

模型选择

根据评测结果,选择qwen2.5-1.5b-instruct作为基座模型。

模型训练

微调框架选择

  • Llama-factory

    • Llama-factory是一个开源的微调框架,用于微调大语言模型。Llama-factory提供了一系列的微调方法,包括QLoRA、P-Tuning、Prefix Tuning等。
  • XTuner

    • XTuner是一个开源的微调框架,用于微调大语言模型。XTuner提供了一系列的微调方法,包括QLoRA、P-Tuning、Prefix Tuning等。相对于Llama-factory,XTuner的微调方法更加灵活,支持更多的微调方法。并且可以在微调过程中直观的进行主观评估。

由于该项目对微调结果的评估方式主要为主观评估,所以选择XTuner作为微调框架。

XTuner安装

使用以下命令安装XTuner:

bash 复制代码
conda create -n xtuner python=3.10 -y
conda activate xtuner
git clone -c http.proxy=http://127.0.0.1:7890  https://github.com/InternLM/xtuner.git
cd xtuner/
pip install torch==2.5.1 torchvision==0.20.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cu121 --proxy=http://127.0.0.1:7890
pip install -e .

微调方法选择

  • LoRA
    • 核心思想:
      • 低秩分解(Low-Rank Decomposition):将原始的大矩阵分解为两个低秩矩阵的乘积,其中一个矩阵的秩远小于原始矩阵的秩。
  • QLoRA
    • 相对于LoRA,QLoRA在训练过程中进行了量化,减少了内存占用和计算量。

本项目中选择QLoRA作为微调方法。

训练数据预处理

在《数据工程篇》生成的数据是csv格式,需要将其转换为XTuner支持的json格式。并且将其分为训练集、测试集和验证集。转换脚本如下:

python 复制代码
import csv
import json
import random

def convert_csv_to_json(csv_file_path, train_json_path, test_json_path, val_json_path):
    # 读取CSV文件
    with open(csv_file_path, 'r', encoding='utf-8') as csv_file:
        csv_reader = csv.DictReader(csv_file)
        data = list(csv_reader)
    
    # 随机打乱数据
    random.shuffle(data)
    
    # 分割数据集
    total = len(data)
    train_size = int(total * 0.8)
    remaining = total - train_size
    
    train_data = data[:train_size]
    test_data = data[train_size:train_size + remaining//2]
    val_data = data[train_size + remaining//2:]
    
    # 转换格式并保存训练集
    train_result = []
    for row in train_data:
        conversation = {
            "conversation": [
                {
                    "input": row["user"],
                    "output": f"{row['style']}\n{row['assistant']}"
                }
            ]
        }
        train_result.append(conversation)
    
    with open(train_json_path, 'w', encoding='utf-8') as json_file:
        json.dump(train_result, json_file, ensure_ascii=False, indent=2)
    
    # 转换格式并保存测试集
    test_result = []
    for row in test_data:
        conversation = {
            "conversation": [
                {
                    "input": row["user"],
                    "output": f"{row['style']}\n{row['assistant']}"
                }
            ]
        }
        test_result.append(conversation)
    
    with open(test_json_path, 'w', encoding='utf-8') as json_file:
        json.dump(test_result, json_file, ensure_ascii=False, indent=2)
    
    # 转换格式并保存验证集
    val_result = []
    for row in val_data:
        conversation = {
            "conversation": [
                {
                    "input": row["user"],
                    "output": f"{row['style']}\n{row['assistant']}"
                }
            ]
        }
        val_result.append(conversation)
    
    with open(val_json_path, 'w', encoding='utf-8') as json_file:
        json.dump(val_result, json_file, ensure_ascii=False, indent=2)

if __name__ == "__main__":
    input_csv = "style_chat_data.csv"
    train_json = "train.json"
    test_json = "test.json"
    val_json = "val.json"
    
    convert_csv_to_json(input_csv, train_json, test_json, val_json)
    print(f"转换完成,结果已保存到 {train_json}, {test_json}, {val_json}")

该脚本能够将style_chat_data.csv文件中的数据按80%训练集、10%测试集和10%验证集的比例分割,并转换为指定的JSON格式。脚本会生成train.json、test.json和val.json三个文件。转换后的json文件部分内容如下:

json 复制代码
[
  {
    "conversation": [
      {
        "input": "有家的感觉,喜欢!",
        "output": " 温柔\n 太好了呢!能给你带来温暖和安慰,我也很开心💖。有什么想聊的话题都可以和我分享哦。"
      }
    ]
  },
  {
    "conversation": [
      {
        "input": "有家的感觉,喜欢!",
        "output": " 温柔\n 太好了呢!我很高兴能给你带来温暖和安慰,如果有任何问题,随时告诉我哦~💖"
      }
    ]
  }
]

训练参数设置

  1. 模型设置

    • pretrained_model_name_or_path
      • 模型路径:/mnt/model/Qwen/Qwen2___5-1___5B-Instruct
  2. 训练集设置

    • data_files
      • 训练集路径:/mnt/datas/output.json
    • max_length
      • 最大长度:256
  3. 训练参数设置

    • batch_size
      • 批次大小:20
    • max_epochs
      • 最大迭代次数:10000
  4. 验证问题设置

    • evaluation_inputs
      • 验证问题设置(从验证集里随机选择10个问题):
        • 工作忙的没有私人时间,生活已经变得不会再幸福了。
        • 进医院你陪钱
        • 那个社长明信片还没收到么
        • 老林有对象我都替他开心
        • 静静的校园真美,羡慕
        • 穿这衣服,会被人领走的
        • 外星人还有台式呢?
        • 今天都没来道早安作业一大堆啊写死了
        • 居然有情人一起回去
        • 狗东西吃这么好
  5. 训练策略设置

    • quantization_config
      • 量化配置:
        • load_in_4bitFalse (禁用4位量化)
        • load_in_8bitTrue (启用8位量化)
    • lora
      • LoRA配置:
        • r64
        • lora_alpha128
  6. 数据配置

    • dataset
    • 数据集配置:
      • typeload_dataset
      • pathjson
      • data_filesdata_files
    • dataset_map_fn
      • 数据集映射函数配置:None

开始训练

将上面的参数保存到配置文件中,然后使用以下命令开始训练:

bash 复制代码
xtuner train qwen2_5_1_5b_chat_qlora_alpaca_e3.py

下面内容分别显示训练前期,中期和后期的验证集测试效果:

  • 训练前期:
txt 复制代码
用户输入:穿这衣服,会被人领走的
模型输出:这衣服,会被人领走的,这句话可能是在提醒你注意自己的穿着,避免引起不必要的注意。如果你指的是某种特定的场合或环境,可能需要根据实际情况来决定是否穿得过于暴露或吸引人。在公共场合,保持适当和尊重他人的穿着是很重要的。
用户输入:外星人还有台式呢?
模型输出:外星人可能没有台式电脑,因为他们的科技水平和我们完全不同。他们可能使用更先进的设备,比如光子计算机或者量子计算机。不过,这只是一个假设,因为我们目前还没有发现外星人。
用户输入:今天都没来道早安作业一大堆啊写死了
模型输出:今天没来道早安作业,感觉好累啊。写得太多了,是不是有点超负荷了呢?别担心,慢慢来,休息一下,调整一下状态,明天继续加油哦!
用户输入:居然有情人一起回去
模型输出:起来像是在描述一段感情的结束,你是否需要一些安慰或建议?

此时模型的输出完全没有我们需要的风格,所以需要进行微调。

  • 训练中期:
txt 复制代码
用户输入:穿这衣服,会被人领走的
模型输出: 温柔
穿得这么漂亮,一定很自信呢!别担心,你很吸引人的。
用户输入:外星人还有台式呢?
模型输出: 外星人当然有台式电脑啦!他们可能有自己的科技,但人类的台式电脑也是他们了解我们文明的重要工具呢。你有没有什么关于外星人的有趣故事想分享呢?
用户输入:今天都没来道早安作业一大堆啊写死了
模型输出: 毒舌
6,你这是在用早安作业当早安吗?栓Q,别累着自己,早点休息,明天精神点!🔥
用户输入:居然有情人一起回去
模型输出: 毒舌
6,你这是在上演《情书》里的剧情吗?栓Q,别让爱情耽误了回家的路。

此时模型的输出已经有了我们需要的风格,但是还是有一些问题,所以需要继续微调。

  • 训练后期:
txt 复制代码
用户输入:穿这衣服,会被人领走的
模型输出: 温柔
 这句话听起来你有点担心自己的穿着哦。但重要的是你穿得开心啦!🌸你喜欢什么样的风格呀?
用户输入:外星人还有台式呢?
模型输出: 温柔
 外星人通常是指外星生物,它们的设计和外观可能与地球人不同,至于"台式"是哪个词呢?如果你感到困惑,我可以帮你查查相关信息哦。🤔💖
用户输入:今天都没来道早安作业一大堆啊写死了
模型输出: 温柔
 哎呀,听起来你今天真的很忙呢。工作虽然忙但记得也要照顾好自己哦,休息很重要。如果需要帮忙或者想聊聊天,我都在这里。加油!💖
用户输入:居然有情人一起回去
模型输出: 温柔
 那真是一件幸福的事情呢!希望你们之间的感情永远像现在一样美好哦~💖

此时模型的输出已经完全符合我们需要的风格,可以停止训练。

  • 训练loss曲线

可以看到在训练轮次达到2000左右时,模型的loss曲线开始收敛,并且loss值在0.07左右。

在训练过程中中断了一次,所以loss曲线有一个小的向上尖峰。

模型转换

XTuner训练的权重是pth格式的,需要将其转换为huggingface格式。

使用以下命令将模型转换为huggingface格式:

bash 复制代码
xtuner convert pth_to_hf \
    /mnt/xtuner/qwen2_5_1_5b_chat_qlora_alpaca_e3.py \
    /mnt/xtuner/work_dirs/qwen2_5_1_5b_chat_qlora_alpaca_e3/iter_24500.pth \
    /mnt/xtuner/work_dirs/qwen2_5_1_5b_chat_qlora_alpaca_e3/iter_24500

合并模型

转换完成后,需要和基座模型进行合并,得到最终的模型。

使用以下命令合并模型:

bash 复制代码
xtuner convert merge \
    /mnt/model/Qwen/Qwen2___5-1___5B-Instruct \
    /mnt/xtuner/work_dirs/qwen2_5_1_5b_chat_qlora_alpaca_e3/iter_24500 \
    /mnt/model/Qwen/Qwen2___5-1___5B-Instruct_Merge

模型训练效果评估

下面我们可以对微调后的模型进行测评。测评方法为主观评测。

先使用以下命令运行模型:

bash 复制代码
lmdeploy chat \
  --chat-template /mnt/datas/chat_template.json \
  /mnt/model/Qwen/Qwen2___5-1___5B-Instruct_Merge

然后使从测试集内随机选择10个问题,进行测试。以下是部分测试结果:

txt 复制代码
问:谢谢你,陪在我身边,一句好朋友。我们一起努力
答: 温柔
 谢谢你,我的朋友💖 我们都会一起加油,向着更好的未来前进!🌸

问:看看哪能淘个护士给你吧
答: 毒舌
 栓Q,你这是想把我培养成医院吉祥物啊?6到飞起!🔥蚌埠住了,其实医生太忙,需要休息。

问:和老师的发型显老
答: 温柔
 每个人的风格都有独特的韵味呢,不必太在意啦。老师的发型可能更添成熟魅力哦。💖

问:那个三色丸子是不是不好吃?
答: 毒舌
 好家伙,你吃的不是丸子,是悲伤的五彩斑斓球啊,栓Q,下次我请你去吃大当家。🥢🔥

问:走,去北极
答: 温柔
 哇,去北极探险听起来好刺激呀!但记得保暖哦,我没有耳朵的地方你可以想象一下。😊🌸

总结

本文详细介绍了大模型对话风格微调项目的完整流程,包括模型选型、评测、训练和效果评估。项目选用Qwen2-1.5B模型,通过XTuner框架进行QLoRA微调,成功实现了"温柔"和"毒舌"两种对话风格的迁移,并通过主观评测验证了微调效果。下一篇文章将介绍模型部署的流程。

相关推荐
江畔柳前堤3 小时前
roLabelImg 详细安装教程
开发语言·人工智能·后端·云原生
阿里云大数据AI技术4 小时前
分链路差异化设计的DSP准实时数仓|钛动科技基于阿里云实时计算 Flink 版 + DLF Paimon + EMR Serverless StarRocks 的实践
人工智能·flink
陕西企来客4 小时前
2026年7月AI智能搜索曝光趋势研判
大数据·人工智能·机器学习·ai智能搜索曝光
阿里云大数据AI技术4 小时前
从算力到智能体,面向 Agentic AI 的基础设施演进
人工智能·agent
hangyuekejiGEO5 小时前
GEO技术服务选型指南
大数据·人工智能·python
阿里云大数据AI技术5 小时前
EMR Serverless Spark AI Function 的双维降本实践
人工智能·sql·spark
陈随易6 小时前
moon,apt和yum之外linux系统命令安装新选择
前端·后端·程序员
维基框架6 小时前
GitHub源码处理提速 一趟扫描反而更慢
人工智能·github
冬奇Lab6 小时前
代码库知识库系列(05):向量检索 vs 知识图谱——加了调用图并没有变更好
人工智能
AKAMAI6 小时前
你的源服务器可能是你做出的最昂贵决定
运维·人工智能·云计算