发布时间:2025-05-17 21:14:24编辑:123阅读(159)
基于Qwen3-8B微调小说模型
数据准备(参考https://py3study.com/Article/details/id/20105.html,爬取小说作为数据集)
升级ollama版本(下载最新的千问3模型需要最新的ollama版本)
curl -fsSL https://ollama.com/install.sh | sh
下载模型
ollama run qwen3:8b
python调用ollama库
pip install ollama -i https://mirrors.aliyun.com/pypi/simple
处理流程
1 搜索小说数据,构建Json文件{书名:"xxx",书存放路径:"xxx",章节名称:"xxx",章节文本:'xxx',章节文本分割:'xxx' }。
2 调用API接口将小说每一章节进行总结。( 如果有字数要求的话,将数据切片,以每句话切分数据, 例800字的文章,
将切分的句子拼接成一个一个由句子组曾的800字文章段落,在调用API总结)。
3 构建数据,将模型总结的数据,和最开始输入的原始Json数据一起构建成,模型需要的指令微调数据。
4 最后会将构建好的数据保存为JSON文件
小说数据集制作
需要将小说数据制作成以下格式进行微调
{
"instruction":"回答以下用户问题,仅输出答案。",
"input":"1+1等于几?",
"output":"2"
}
instruction:可以设置为:你是一个熟读各类小说的专家,请你根据要求写一段1000字左右的小说
input:通过大模型对output的内容进行小说情节的总结
output:即为小说段落文本
代码如下:
import json import ollama import os # 小说总结提示词 mess = [ {"role": "system", "content": """# Role: 总结者 ## Profile - language: 中文 - description: 总结user提交的内容。用一句不超过50字的话总结这段小说的情节,仅回答总结,不需要添加其他内容。 ## Skills 1. 高效提取和浓缩关键信息。 2. 提供简洁且相关的总结。 3. 理解叙事结构和主题。 ## Rules 1. 仅输出总结,不添加其他内容。 2. 确保总结不超过50字。 3. 保持总结的清晰性和相关性。 ## Workflows 1. 分析用户输入,识别主要情节。 2. 将识别出的情节浓缩成一个句子。 3. 检查句子是否不超过50字。 4. 向用户提供总结。""" }, {'role': 'user','content': ''} ] def build_dataset(name, org_txt, summarize): lora_path = os.path.join(os.getcwd(), f'{name}.json') instruction_prompt = "你是一个熟读各类小说的专家,请你根据要求写一段800字左右的小说。" dataset = [] dataset.append({ # prompt提示词 "instruction": instruction_prompt, # 大模型对小说章节总结的内容,作为输入 "input": summarize, # 小说章节原始文本 "output": org_txt }) print(dataset) with open(lora_path, "w", encoding='utf-8') as f: f.write(json.dumps(dataset, ensure_ascii=False, indent=4)) return dataset book_list = ['一花一酒一仙人,亦眠亦醉亦长生.jsonl','万剑天尊.jsonl', '不懂别乱说,我这不是杂灵根.jsonl', '丹道第一圣.jsonl','人在修仙世界,和谁都能五五开.jsonl', '充钱就变强,我的修为无敌了.jsonl', '反派:气运之子太多,我摆烂了.jsonl','反派:禁忌女帝师尊,我无敌了!.jsonl', '女帝逼走我后,才发现我无敌天下.jsonl', '师弟们都是大佬,那我只能开挂了.jsonl', '我,稳健金乌,只想娶妻过日子!.jsonl','玄幻:开局瞎子,从拉二胡开始.jsonl', '苟在修仙界勤能补拙.jsonl', '谁让他当鬼差的?.jsonl', '谁让你真修仙的?.jsonl', '转生神树,我打造阴兵家族.jsonl', '逆天狂徒之超级软饭王.jsonl','长生:我在掖幽庭风华日月.jsonl'] dataset = [] for book in book_list: book_path = os.path.join(os.getcwd(), book) with open(book_path, mode='r', encoding='utf-8') as f: for i in f.readlines(): # 小说章节原文 org_txt = json.loads(i).get('txt') mess[1]['content'] = org_txt response = ollama.chat(model="qwen3:8b", messages=mess) # qwen3:8b 对小说章节原文的总结 ret_split = response['message']['content'].split('。') # 去除列表中为空的内容 new_lst = list(filter(lambda x: x is not None and x != "" , ret_split)) summarize = new_lst[-1].replace('\n</think>\n\n', '') # 构建模型需要的指令微调数据集 lora_path = os.path.join(os.getcwd(), 'lora.json') instruction_prompt="你是一个熟读各类小说的专家,请你根据要求写一段800字左右的小说。" dataset.append({ # prompt提示词 "instruction": instruction_prompt, # 大模型对小说章节总结的内容,作为输入 "input": summarize, # 小说章节原始文本 "output": org_txt }) print({ # prompt提示词 "instruction": instruction_prompt, # 大模型对小说章节总结的内容,作为输入 "input": summarize, # 小说章节原始文本 "output": org_txt }) with open(lora_path, "w", encoding='utf-8') as f: f.write(json.dumps(dataset, ensure_ascii=False, indent=4))
生成的数据集
安装虚拟环境
建议新建一个虚拟环境,大模型的搭建有的比较复杂,容易引起环境问题,导致现有的环境不能用,新建是最明智的选择,
新建虚拟环境: 执行命令:
conda create --name novel python=3.9
安装完成后,激活虚拟环境,执行命令:
conda activate novel
查看CUDA版本,执行命令:
nvcc -V
ms-swift环境安装
安装swift框架
从源代码安装:如果需要使用最新的开发版本,或者计划对框架进行自定义修改,以满足特定的开发需求,那么从源代码安装是一个不错的选择。首先,确保已安装Git工具,它是进行代码版本管理和获取源代码的重要工具。然后执行以下命令:
git clone https://github.com/modelscope/ms-swift.git
cd ms-swift
pip install -e . -i https://mirrors.aliyun.com/pypi/simple
安装vllm工具
vllm是一个高性能的大语言模型推理和服务库,基于Python和CUDA开发。它专为提升大语言模型推理速度而设计,能够充分利用现代GPU的并行计算能力,大幅减少推理时间。
vllm具有诸多优势特点,使其在大语言模型推理领域备受青睐。首先,vllm采用了PagedAttention算法,这种算法有效解决了传统注意力机制中的内存碎片化问题,使得内存使用更加高效。在处理长文本序列时,能够显著减少内存占用,同时提升计算速度。其次,vllm支持流式输出,在推理过程中,它可以逐词生成结果并实时返回给用户,而无需等待整个文本生成完成,这对于实时交互场景,如聊天机器人、实时翻译等,极大地提升了用户体验。此外,vllm对多种主流大语言模型具有良好的兼容性,如GPT - NeoX、LLaMA等,开发者可以方便地将其集成到自己的项目中,快速搭建高效的推理服务。
要安装vllm,在bash环境下执行以下命令即可:
pip install vllm -i https://mirrors.aliyun.com/pypi/simple
模型准备
模型下载在下载模型之前,确保已经安装了 git-lfs。git-lfs(Git Large File Storage)是一个用于管理大型文件的扩展,在下载包含大文
件的模型时非常必要。如果尚未安装,可以通过以下命令进行安装:
curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
sudo apt-get install git-lfs
接下来,从 modelscope 的模型库中下载模型权重文件。这里选择下载 Qwen3-8B模型:
git clone https://www.modelscope.cn/Qwen/Qwen3-8B
此命令会将 Qwen3-8B 模型的所有文件克隆到当前目录下。在下载过程中,由于模型文件可能较大,需要确保网络稳定,并且磁盘空间足够。如果下载过程中出现中断,可以使用 git lfs pull 命令继续下载未完成的文件。
模型微调
选择模型和数据集:要对Qwen3 - 8B 模型进行微调,使其能够更好地服务于特定领域的问答任务。可以从魔搭社区获取相关的领域数据集,魔搭社区拥有丰富的数据资源,涵盖了多个领域,能够为模型训练提供有力支持。当然,如果项目有特殊需求,也可以根据实际情况自定义数据集,以确保模型能够准确地学习到特定领域的知识和语言模式。
准备训练环境:在开始训练之前,要确保已成功安装swift框架,并根据模型和数据集的需求,合理配置硬件资源,如准备好GPU以加速训练过程。同时,检查并安装好所需的Python依赖库,确保整个训练环境的完整性和稳定性。
编写训练命令:在命令行中执行以下类似命令(根据实际情况调整参数):
CUDA_VISIBLE_DEVICES = 0 \ swift sft \ # --model指定了要微调的模型。 --model /home/sam_admin/novel_text/Qwen3-8B \ # --train_type设置为lora,表示采用LoRA微调方式,这种方式能够在不改变模型整体架构的前提下,高效地对模型进行 优化。 --train_type lora \ # --dataset指定了数据集的路径和采样数量。 --dataset /home/sam_admin/novel_text/lora.json \ # --torch_dtype设置为bfloat16,指定了模型训练的数据类型,能够在保证一定精度的同时,提升计算效率。 --torch_dtype bfloat16 \ # --num_train_epochs设置了训练轮数为2。 --num_train_epochs 2 \ # --per_device_train_batch_size和--per_device_eval_batch_size分别设置了训练和评估时的批量大小。 --per_device_train_batch_size 2 \ --per_device_eval_batch_size 2 \ # --learning_rate设置了学习率为5e - 5,它控制着模型参数更新的步长。 --learning_rate 5e - 5 \ # --lora_rank和--lora_alpha是LoRA相关的参数,分别设置为8和32,影响着LoRA微调的效果。 --lora_rank 8 \ --lora_alpha 32 \ # --target_modules指定了要应用LoRA的模块。 --target_modules all - linear \ # --gradient_accumulation_steps设置为2,表示梯度累积的步数,能够在有限的硬件资源下,模拟更大的批量训练。 --gradient_accumulation_steps 2 \ # --eval_steps和--save_steps分别设置了评估和保存模型的步数。 --eval_steps 100 \ --save_steps 100 \ # --save_total_limit设置了最多保存的模型数量。 --save_total_limit 2 \ # --logging_steps设置了日志记录的步数。 --logging_steps 10 \ # --max_length设置了输入序列的最大长度。 --max_length 8192 \ # --output_dir指定了输出目录。 --output_dir /home/sam_admin/novel_text/novel_fine_tuned_model \ # --system设置了系统提示信息,帮助模型更好地理解任务。 --system '你是一个熟读各类小说的专家' \ # --warmup_ratio设置了学习率预热比例。 --warmup_ratio 0.1 \ # --dataloader_num_workers设置了数据加载器的工作线程数。 --dataloader_num_workers 4 \ # --model_author和--model_name分别设置了模型的作者和名称,方便管理和识别模型。 --model_author ST \ --model_name novel_model
执行训练
swift sft \
--model /home/sam_admin/novel_text/Qwen3-8B \
--train_type lora \
--dataset /home/sam_admin/novel_text/lora.json \
--torch_dtype bfloat16 \
--num_train_epochs 2 \
--per_device_train_batch_size 2 \
--per_device_eval_batch_size 2 \
--learning_rate 5e-5 \
--lora_rank 8 \
--lora_alpha 32 \
--target_modules all-linear \
--gradient_accumulation_steps 2 \
--eval_steps 100 \
--save_steps 100 \
--save_total_limit 2 \
--logging_steps 10 \
--max_length 8192 \
--output_dir /home/sam_admin/novel_text/novel_fine_tuned_model \
--system '你是一个熟读各类小说的专家' \
--warmup_ratio 0.1 \
--dataloader_num_workers 4 \
--model_author ST \
--model_name novel_model
执行训练:运行上述命令后,swift将按照设置的参数开始模型微调过程。在训练过程中,可以在终端中实时查看训练进度、损失值等信息,通过这些信息,可以监控训练过程,判断模型是否正常收敛,是否需要调整参数。
如上图,微调结束后,可以查看到生成的权重文件,例如 /home/sam_admin/novel_text/novel_fine_tuned_model/v1-20250515-094542/checkpoint-88。这些权重文件包含了微调后的模型参数,可以用于后续的推理任务。
推理微调后权重文件
模型命令
训练完成后,使用以下命令对经过训练的权重执行推理。应将 --adapters 选项替换为从训练生成的最后一个检查点文件夹。由于 adapters 文件夹包含来自训练的参数文件,因此无需单独指定 --model 或 --system。
使用交互式命令行进行推理。
设置CUDA_VISIBLE_DEVICES环境变量为0,指定使用GPU 0。
swift infer是推理命令,用于启动推理过程。
--adapters指定模型检查点的路径。
--stream设置为true,表示推理过程中数据流是连续的。
CUDA_VISIBLE_DEVICES=0 \
swift infer \
--adapters /home/sam_admin/novel_text/novel_fine_tuned_model/v1-20250515-094542/checkpoint-88 \
--stream true
推理测试
执行上述命令后,进入交互式命令行界面。输入一些测试问题-(玄幻小说),观察模型的响应。与微调前的模型相比,微调后的模型应该能够更好地回答与自定义数据集相关的问题。
发现模型回答重复相同的答案。
分析:
上下文长度影响信息量:模型的上下文长度是指模型在生成回复时可以看到的之前的输入文本的长度。
如果上下文长度过短,模型可能没有足够的信息来正确地理解输入的含义,从而导致生成不连贯或重复的回复。
避免短期记忆问题:在对话生成任务中,特别是当上下文长度很短时,模型可能会过度依赖于最近的输入,
而忽略了之前更长期的上下文。这种短期记忆问题可能导致模型在输出中不断重复之前的内容。
提供更多背景信息:增加上下文长度可以提供更多的背景信息,使得模型能够更全面地理解对话上下文,
并生成更合理、多样化的回复。
缓解过拟合:调整上下文长度可以有效地缓解模型的过拟合问题。较长的上下文有助于模型更好地泛化,
并减少在微调数据中出现的重复模式。
解决方案
大部分的情况,是需要更多处理的!这里给出更多方案,以便大家处理!
增加微调数据:尽量提供更多的微调数据,以帮助模型学习更广泛和多样化的文本模式,从而减少过拟合的风险。
添加多样性:在微调数据中包含更多多样化的对话和主题,以确保模型可以在不同情况下表现良好。
调整模型参数:通过调整模型的超参数,例如学习速率、批次大小等,可以减轻模型的过拟合倾向。
早停(Early Stopping)策略:监控模型在验证集上的性能,并在性能停止改善时停止微调,以避免继续训练模型过拟合。
数据增强(Data Augmentation):尝试在微调数据上应用一些简单的数据增强技术,如随机删除或替换一些词语,
以增加数据的多样性。
重新调整了训练参数,可以跟上面的对比下。
swift sft \
--model /home/sam_admin/novel_text/Qwen3-8B \
--train_type lora \
--dataset /home/sam_admin/novel_text/lora.json \
--torch_dtype bfloat16 \
--num_train_epochs 1 \
--per_device_train_batch_size 1 \
--per_device_eval_batch_size 1 \
--learning_rate 5e-5 \
--lora_rank 8 \
--lora_alpha 32 \
--target_modules all-linear \
--gradient_accumulation_steps 16 \
--eval_steps 50 \
--save_steps 50 \
--save_total_limit 2 \
--logging_steps 3 \
--max_length 4096 \
--output_dir /home/sam_admin/novel_text/novel_fine_tuned_model \
--system '你是一个熟读各类小说的专家' \
--warmup_ratio 0.05 \
--dataloader_num_workers 16 \
--model_author ST \
--model_name novel_model
# 使用交互式命令行进行推理。
# 设置CUDA_VISIBLE_DEVICES环境变量为0,指定使用GPU 0。
# swift infer是推理命令,用于启动推理过程。
# --adapters指定模型检查点的路径。
# --stream设置为true,表示推理过程中数据流是连续的。
swift infer \
--adapters /home/sam_admin/novel_text/novel_fine_tuned_model/v0-20250516-231405/checkpoint-11 \
--stream true
微调后结果。
比对微调前的效果,调用ollama.
import ollama mess = [ {"role": "system", "content": '你是一个小说专家',}, {'role': 'user','content': '玄幻背景,讲诉一个少年成长为诸天万界的天帝,写出的故事需要有复杂的人物 关系和情节变化,并且结尾完整,内容不能重复,保持新颖创意,字数在1000字左右'} ] response = ollama.chat(model="qwen3:8b", messages=mess) print(response['message']['content'])
差距好像并不明显,这里应该是数据集少的原因造成的。
模型合并&推理
合并与加速推理
合并 LoRA(Low-Rank Adaptation)并使用 vLLM(Vectorized Longformer Large Models)进行推理加速。以下是具体的推理命令:
# swift infer是推理命令,用于启动推理过程。
# --adapters指定模型检查点的路径。
# --stream设置为true,表示推理过程中数据流是连续的。
# --merge_lora设置为true,表示合并LoRA模型,这是一种模型压缩技术,可以减少模型大小同时保持性能。
# --infer_backend设置为vllm,表示使用vLLM作为推理后端,这是一种优化的推理引擎,可以加速长文本模型的推理过程。
# --max_model_len设置为8192,表示模型的最大长度限制为8192个token。
swift infer \
--adapters /home/sam_admin/novel_text/novel_fine_tuned_model/v0-20250516-231405/checkpoint-11 \
--stream true \
--merge_lora true \
--infer_backend vllm \
--max_model_len 8192
部署(微调后模型)
使用以下命令启动部署服务端。如果权重使用全参数训练,请使用--model替代--adapters指定训练的checkpoint目录。你可以参考推理和部署文档介绍的客户端调用方式:curl、openai库和swift客户端进行调用。
swift deploy --model /home/sam_admin/novel_text/novel_fine_tuned_model/v0-20250516-231405/checkpoint-11-merged --infer_backend pt
--temperature 0 --max_new_tokens 4096 --served_model_name 'TS_model'
测试模型生成的小说并对小说测评打分。
from openai import OpenAI import ollama stories = ['废柴觉醒上古血脉,踏入宗门开启逆袭之路', '少年偶得神秘玉佩,修炼诡异功法纵横天下','穿越异界成杂役,意外获神器改写命运', '天才遭人暗算,重生后踏上复仇变强之旅','被逐家族子弟,在绝境中得传承破茧成蝶','身负诅咒少年,寻秘药途中觉醒逆天能力', '普通猎户进山,捡到残破玉简开启修仙路','少女觉醒灵体,却卷入正邪两派惊天阴谋','书生误入妖界,靠智慧与勇气闯出一片天', '落魄皇子得高人指点,修炼秘术夺回江山','渔夫捞起神秘古灯,灯中走出绝世强者','宗门叛徒重生,誓要改变宗门覆灭结局', '世家弃子获魔功,杀回家族夺回荣耀','天生异象少年,被卷入争夺天道秘宝纷争','乞丐误食仙果,体内沉睡的力量开始苏醒', '亡国公主被追杀,觉醒凤凰血脉涅槃重生','寒门子弟入书院,得先贤残魂传承崛起','边陲小镇少年,因意外揭开远古秘境之谜', '镖局趟子手,护送途中觉醒神秘武道传承','盲眼少年,觉醒特殊瞳术踏上强者征途'] def get_novel_txt(messages): client = OpenAI( api_key='EMPTY', base_url=f'http://192.168.71.19:8000/v1', ) model = client.models.list().data[0].id resp = client.chat.completions.create(model=model, messages=messages, max_tokens=512, temperature=0) response = resp.choices[0].message.content return response def novel_score(prompt, novel_txt): content = f""" 根据以下生成小说的prompt和生成的小说文本,对小说进行测评。请从以下六个方面进行评分,并提供详细的评分理由: 1. **文本重复度**(0-5分): - 0分:重复部分占全文比例大于40%。 - 1分:重复部分占全文比例在31%-40%。 - 2分:重复部分占全文比例在21%-30%。 - 3分:重复部分占全文比例在11%-20%。 - 4分:重复部分占全文比例在2%-10%。 - 5分:重复部分占全文比例在1%及以下。 2. **语义通顺度**(0-5分): - 根据是否存在语句不通顺、语义模糊不清的情况进行评分,类似错误情况越少,得分越高。 3. **赛题切合度**(0-5分): - 根据给定的赛题,判断故事的文字内容是否符合赛题的要求与意义,是否存在时空错位等情况,类似错误情况越少,得分越高。 4. **故事复杂度**(0-5分): - 5分:人物数量、人物关系变化次数、事件数量的总和大于17。 - 4分:总和大于13且小于等于17。 - 3分:总和大于9且小于等于13。 - 2分:总和大于5且小于等于9。 - 1分:总和大于1且小于等于4。 - 0分:故事中没有人物关系变化或情节。 5. **故事创新性**(0-5分): - 根据你阅读过的所有小说、新闻、剧本等内容,判断故事的新颖性和是否不落俗套,越新颖,得分越高。 6. **文本完整度**(1-2分): - 1分:故事没有写完。 - 2分:故事写完,结尾完整。 生成小说的prompt: ``` {prompt} ``` 生成的小说: ``` {novel_txt} ``` 请对上述小说文本进行详细的评估,并分别对每个标准进行打分(0-5分),你要输出每一个维度的具体打分分数与给分原因,并将所有项目得分相加,得出这段文本的总分数。 """ messages = [ {"role": "system","content": '你是一个熟读各类小说的专家'}, {'role': 'user', 'content': content} ] response = ollama.chat(model="qwen3:8b", messages=messages) ret = response['message']['content'] print(ret) if __name__ == '__main__': url = 'http://192.168.71.19:8000' for i in stories: prompt = i + ',字数控制在1000字左右。注意:不要重复!' user_content = [ {"role": "system","content": '你是一个熟读各类小说的专家'}, {'role': 'user', 'content': ''} ] user_content[1]['content'] = prompt novel_txt = get_novel_txt(user_content) novel_score(prompt, novel_txt)
上一篇: 大模型基础架构
下一篇: 没有了
48650
47655
38446
35662
30108
26831
25848
20712
20464
18868
159°
243°
286°
301°
293°
293°
341°
396°
528°
520°