ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于《甄嬛传》剧本的个性化 AI 实战:Chat-嬛嬛 LoRA 微调全流程(self-llm 项目案例)

2026/9/19 5:10:16 拓冰建站 浏览量
基于《甄嬛传》剧本的个性化 AI 实战:Chat-嬛嬛 LoRA 微调全流程(self-llm 项目案例) 基于《甄嬛传》剧本的个性化 AI 实战Chat-嬛嬛 LoRA 微调全流程self-llm 项目案例【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/datawhalechina/self-llm本篇文章完整复现 Datawhale/self-llm 仓库中的 Chat-嬛嬛 案例从《甄嬛传》剧本原始台词出发经过角色对话抽取、指令数据集构造、LLaMA3.1-8B-Instruct 模型 LoRA 微调、再到加载 LoRA 权重与嬛嬛对话打通小说/剧本 → 角色人设 AI的个性化大模型训练完整链路。读完本文你将掌握角色对话数据构造方法、LoRA 训练脚本的每一处关键参数以及如何把这套流程迁移到任意角色、任意基座模型上。项目背景Chat-嬛嬛 与个性化 AI 的思路Chat-嬛嬛又名 Chat-甄嬛是利用《甄嬛传》剧本中所有关于甄嬛的台词和语句基于大模型进行LoRA 微调得到的、能够模仿甄嬛语气的聊天语言模型。甄嬛是小说《后宫·甄嬛传》与电视剧《甄嬛传》的女一号其人物弧光从入宫选秀、斗垮华妃到遭皇后暗算、出宫为尼再到复宠回宫、扳倒幕后黑手最终成为权倾朝野的圣母皇太后——丰富且风格鲜明的台词使其成为极具辨识度的角色微调语料。该案例的深层价值在于它并非只为复刻一个嬛嬛而是以《甄嬛传》为切入点打造了一套基于小说、剧本的个性化 AI 微调大模型完整流程——提供任一小说或剧本指定人物角色运行本项目完整流程即可让每位用户基于心仪的作品打造一个契合角色人设、具备高度智能的个性化 AI。据原项目文档记载Chat-嬛嬛 模型累计下载量 15.6k并先后获得 2023 讯飞星火杯人认知大模型场景创新赛 Top50、2024 书生·浦语大模型挑战赛春季赛创意应用奖 Top12。整体技术路线概览整个案例分为四个步骤与本仓库 Examples 导读 中实际应用示范的定位一致环境准备搭建 Python/PyTorch/依赖库环境数据准备从剧本台词中抽取角色对话构造instruction/input/output指令数据集模型训练基于 LLaMA3.1-8B-Instruct 调用 LoRA 微调脚本训练推理测试加载 LoRA 权重与角色对话验证效果。对应到仓库文件本案例的核心资产集中在 examples/Chat-嬛嬛/教程文档与 train.py 训练脚本和 dataset/甄嬛指令数据集下文将逐一深入。Step 1环境准备本文的基础环境如下---------------- ubuntu 22.04 python 3.12 cuda 12.1 pytorch 2.3.0 ----------------默认学习者已安装好以上 PytorchCUDA环境如未安装请自行安装。首先pip换源加速下载并安装依赖包# 升级pip python -m pip install --upgrade pip # 更换 pypi 源加速库的安装 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip install modelscope1.16.1 pip install transformers4.43.1 pip install accelerate0.32.1 pip install peft0.11.1 pip install datasets2.20.0各依赖在本流程中的职责如下modelscope从 ModelScope 模型库下载 LLaMA3.1 基座模型transformers加载模型与 tokenizer、定义TrainingArguments、驱动Trainer训练peft提供LoraConfig、get_peft_model、PeftModel是 LoRA 微调与推理加载的核心库datasets将处理好的数据包装为Dataset供 Trainer 消费accelerateTrainer 的底层分布式/显存管理支撑。与仓库中 LLaMA3.1 Lora 微调教程 相比本案例依赖版本一致transformers 4.43.x、accelerate 0.32.1、peft 0.11.1、datasets 2.20.0可直接复用同一套环境。Step 2数据准备从剧本台词到指令数据集2.1 分析原始剧本格式《甄嬛传》剧本数据的原始形态是场景 人物名 台词的纯文本例如第2幕 退朝百官散去 官员甲咱们皇上可真是器重年将军和隆科多大人。 官员乙隆科多大人恭喜恭喜啊您可是国家的大功臣啊 官员丙年大将军皇上对你可是垂青有加呀 官员丁年大人您可是皇上的股肱之臣哪 苏培盛追上年羹尧年大将军请留步。大将军—— 年羹尧苏公公有何指教 苏培盛不敢。皇上惦记大将军您的臂伤特让奴才将这秘制的金创药膏交给大人叫您使用。 年羹尧遥向金銮殿拱手臣年羹尧恭谢皇上圣恩敢问苏公公小妹今日在宫中可好啊 苏培盛华妃娘娘凤仪万千、宠冠六宫啊大将军您放心好了。 年羹尧那就有劳苏公公了。转身离去 苏培盛应该的。这种人物名台词的结构非常规整每一句都有人物及对应台词因此可以很轻松地将其处理成对话形式的数据[ {role:官员甲, content:咱们皇上可真是器重年将军和隆科多大人。}, {role:官员乙, content:隆科多大人恭喜恭喜啊您可是国家的大功臣啊}, {role:官员丙, content:年大将军皇上对你可是垂青有加呀}, {role:官员丁, content:年大人您可是皇上的股肱之臣哪}, {role:苏培盛, content:年大将军请留步。大将军——}, ... ]2.2 抽取目标角色构造 QA 问答对在上述对话列表的基础上进一步筛选出我们所关注的角色甄嬛的对话并将其组织为instruction/input/output的问答对前文他人的台词作为instruction甄嬛的回应作为outputinput一般为空。对于此类规整数据使用正则表达式等方法即可快速提取并抽取出目标角色的对话。[ { instruction: 小姐别的秀女都在求中选唯有咱们小姐想被撂牌子菩萨一定记得真真儿的——, input: , output: 嘘——都说许愿说破是不灵的。 }, { instruction: 这个温太医啊也是古怪谁不知太医不得皇命不能为皇族以外的人请脉诊病他倒好十天半月便往咱们府里跑。, input: , output: 你们俩话太多了我该和温太医要一剂药好好治治你们。 }, { instruction: 嬛妹妹刚刚我去府上请脉听甄伯母说你来这里进香了。, input: , output: 出来走走也是散心。 } ]2.3 无规整格式时的处理思路借助大模型抽取对话很多情况下我们并没有剧本这样优秀的台词格式数据而只有一段段的叙述性文本。例如《西游记白话文》的文本形态是...... 原来孙悟空走了以后有一个混世魔王独占了水帘洞并且抢走了许多猴子猴孙。孙悟空听到这些以后气得咬牙跺脚。他问清了混世魔王的住处决定找混世魔王报仇便驾着筋斗云朝北方飞去。 不一会儿孙悟空就来到混世魔王的水脏洞前对门前的小妖喊到∶你家那个狗屁魔王多次欺负我们猴子。我今天来要和那魔王比比高低 小妖跑进洞里报告魔王。魔王急忙穿上铁甲提着大刀在小妖们的簇拥下走出洞门。 孙悟空赤手空拳夺过了混世魔王的大刀把他劈成了两半。然后拔下一把毫毛咬碎喷了出去毫毛变成许多小猴子直杀进洞里把所有的妖精全杀死然后救出被抢走的小猴子放了一把火烧了水脏洞。 ......这类文本中角色对话与叙述混杂就需要借助大模型的能力从整段文本中提取出角色 角色对应的对话然后再筛选出我们需要的角色对话。可以借助extract-dialogue这类开源对话抽取小工具将叙述文本批量转化为结构化对话数据。2.4 仓库数据集实测三种文件形态本仓库在 dataset/ 下提供了最终整理好的甄嬛指令数据集共三种形态可供对照dataset/huanhuan.json约 1.8 万行标准 JSON 数组格式每个元素为{instruction, input, output}是训练主数据dataset/huanhuan-100.json约 500 行的精简版本适合先跑通流程、快速验证dataset/huanhuan.jsonl约 3700 行的 JSONLJSON Lines格式每行一个完整 JSON 对象便于流式读取。三者的字段结构完全一致例如huanhuan.jsonl中的原始记录{instruction:娘娘。,input:,output:你放心本宫到任何时候都不会自轻自贱委屈了这孩子。} {instruction:你是谁,input:,output:我是甄嬛家父是大理寺少卿甄远道。} {instruction:嬛嬛若你肯你还是朕的宠妃朕待你还和从前一样。,input:,output:从前皇上以为还能回到从前吗}可以看到训练语料完整覆盖了甄嬛在不同人生阶段的回应风格——既有对下人的安抚也有对皇上的绵里藏针这正是角色人设 AI 的数据基础。2.5 数据增强让角色对话更丰富除了从剧本直接抽取数据准备环节还可以增加数据增强例如利用两到三条已有数据作为 example 丢给 LLM让其生成风格类似的新数据或者找一部分日常对话的数据集使用 RAG 生成一些固定角色风格的对话数据。这一步可以完全放开大胆尝试是提升角色泛化能力的重要手段。Step 3模型训练基于 LLaMA3.1-8B-Instruct 的 LoRA 微调数据准备完成后训练环节与 self-llm 中其他模型的 LoRA 微调模块高度一致——将数据处理成所需格式调用训练脚本即可。本案例选择LLaMA3.1-8B-Instruct作为基座模型。3.1 模型下载首先下载模型创建model_download.py文件输入以下内容import torch from modelscope import snapshot_download, AutoModel, AutoTokenizer import os model_dir snapshot_download(LLM-Research/Meta-Llama-3.1-8B-Instruct, cache_dir/root/autodl-tmp, revisionmaster)注意记得修改cache_dir为你的模型下载路径。这里使用 ModelScope 的snapshot_download函数第一个参数为模型名称cache_dir为模型下载路径revisionmaster指定主分支版本。下载完成后模型缓存目录名会将模型 ID 中的.转为___如LLM-Research/Meta-Llama-3___1-8B-Instruct后续训练脚本与推理脚本中的路径需与之对应。3.2 训练脚本 train.py 源码解析本案例在 examples/Chat-嬛嬛/train.py 中提供了可直接运行的训练脚本只需修改其中的数据集路径和模型路径即可。下面逐段剖析其实现。① 数据加载JSON → pandas → HuggingFace Dataset# 将JSON文件转换为CSV文件此处实际为读入 DataFrame df pd.read_json(huanhuan.json) ds Dataset.from_pandas(df) tokenized_id ds.map(process_func, remove_columnsds.column_names)脚本用pd.read_json读取 dataset/huanhuan.json或你自定义的角色数据集转换为 pandas DataFrame再通过Dataset.from_pandas包装为 HuggingFaceDataset最后用ds.map(process_func, remove_columnsds.column_names)对每条样本执行格式化处理并移除原始列。② process_func指令数据格式化与 tokenizeprocess_func是数据管线的核心负责把每一条{instruction, input, output}编码成模型训练所需的input_ids / attention_mask / labelsdef process_func(example): MAX_LENGTH 384 # Llama分词器会将一个中文字切分为多个token因此需要放开一些最大长度保证数据的完整性 input_ids, attention_mask, labels [], [], [] instruction tokenizer(f|begin_of_text||start_header_id|system|end_header_id|\n\nCutting Knowledge Date: December 2023\nToday Date: 26 Jul 2024\n\n现在你要扮演皇帝身边的女人--甄嬛|eot_id||start_header_id|user|end_header_id|\n\n{example[instruction] example[input]}|eot_id||start_header_id|assistant|end_header_id|\n\n, add_special_tokensFalse) # add_special_tokens 不在开头加 special_tokens response tokenizer(f{example[output]}|eot_id|, add_special_tokensFalse) input_ids instruction[input_ids] response[input_ids] [tokenizer.pad_token_id] attention_mask instruction[attention_mask] response[attention_mask] [1] # 因为eos token咱们也是要关注的所以 补充为1 labels [-100] * len(instruction[input_ids]) response[input_ids] [tokenizer.pad_token_id] if len(input_ids) MAX_LENGTH: # 做一个截断 input_ids input_ids[:MAX_LENGTH] attention_mask attention_mask[:MAX_LENGTH] labels labels[:MAX_LENGTH] return { input_ids: input_ids, attention_mask: attention_mask, labels: labels }关键设计点LLaMA3.1 Chat Templateinstruction部分按 LLaMA3.1 的对话模板拼接依次包含|begin_of_text|起始符、|start_header_id|system|end_header_id|系统角色其中嵌入 LLaMA3 系列默认的Cutting Knowledge Date: December 2023 / Today Date: ...与角色设定现在你要扮演皇帝身边的女人--甄嬛、user用户输入最后以assistant头结尾等待模型续写。对应模板如下|begin_of_text||start_header_id|system|end_header_id| 现在你要扮演皇帝身边的女人--甄嬛|eot_id||start_header_id|user|end_header_id| 你好呀|eot_id||start_header_id|assistant|end_header_id| 你好我是甄嬛你有什么事情要问我吗|eot_id||start_header_id|assistant|end_header_id|add_special_tokensFalsetokenize 时不再自动添加 BOS 等特殊 token因为模板中已手动写入了|begin_of_text|labels 掩码instruction部分的 labels 全部置为-100PyTorch 交叉熵损失会忽略该值只有output部分的 token 参与损失计算——这是只学习回答、不学习提问的标准做法MAX_LENGTH384Llama 分词器会将一个中文字切分为多个 token因此需要放开一些最大长度以保证数据完整性超长样本统一截断到 384末尾补充pad_token_id并将对应位置 attention_mask 置为 1保证序列对齐。③ 加载模型与 tokenizermodel AutoModelForCausalLM.from_pretrained(./LLM-Research/Meta-Llama-3___1-8B-Instruct, device_mapauto,torch_dtypetorch.bfloat16) model.enable_input_require_grads() # 开启梯度检查点时要执行该方法 tokenizer AutoTokenizer.from_pretrained(./LLM-Research/Meta-Llama-3___1-8B-Instruct, use_fastFalse, trust_remote_codeTrue) tokenizer.pad_token tokenizer.eos_token模型以bfloat16 半精度加载device_mapauto自动分配设备开启gradient_checkpointing时必须调用model.enable_input_require_grads()否则前向传播中的非叶子张量无法获得梯度同时将pad_token指向eos_token避免 padding 时报错。④ LoraConfigLoRA 核心参数config LoraConfig( task_typeTaskType.CAUSAL_LM, target_modules[q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj], inference_modeFalse, # 训练模式 r8, # Lora 秩 lora_alpha32, # Lora alaph具体作用参见 Lora 原理 lora_dropout0.1# Dropout 比例 ) model get_peft_model(model, config) model.print_trainable_parameters() # 打印总训练参数各参数含义与影响参数取值说明task_typeTaskType.CAUSAL_LM模型类型因果语言模型自回归生成target_modules7 个投影层需要训练的层名Attention 的q/k/v/o_proj与 MLP 的gate/up/down_proj覆盖 LLaMA3.1 全部可注入的低秩适配层r8LoRA 的秩控制低秩矩阵的维度秩越大可学习容量越大lora_alpha32LoRA 缩放系数实际缩放因子为lora_alpha / r 4用于调节低秩更新的强度lora_dropout0.1低秩分支的 Dropout 比例用于缓解过拟合inference_modeFalse明确处于训练模式get_peft_model会将原始模型包装为 PEFT 模型model.print_trainable_parameters()会打印可训练参数量LoRA 通常只占总参数的极小比例这正是其高效的原因。⑤ TrainingArguments训练策略args TrainingArguments( output_dir./output/llama3_1_instruct_lora, per_device_train_batch_size4, gradient_accumulation_steps4, logging_steps10, num_train_epochs3, save_steps100, # 为了快速演示这里设置10建议你设置成100 learning_rate1e-4, save_on_each_nodeTrue, gradient_checkpointingTrue )output_dir模型输出路径checkpoint 将写入该目录per_device_train_batch_size4单卡 batch sizegradient_accumulation_steps4梯度累积 4 步等效 batch size 为 16显存不足时减小 batch、增大累积即可logging_steps10每 10 步输出一次日志num_train_epochs3训练 3 个 epochsave_steps100每 100 步保存一次 checkpointlearning_rate1e-4学习率gradient_checkpointingTrue以计算换显存配合model.enable_input_require_grads()使用。⑥ Trainer 训练trainer Trainer( modelmodel, argsargs, train_datasettokenized_id, data_collatorDataCollatorForSeq2Seq(tokenizertokenizer, paddingTrue), ) trainer.train() # 开始训练DataCollatorForSeq2Seq负责将 batch 内样本 padding 对齐训练超长样本时也可考虑paddinglongest以节约显存调用trainer.train()开始训练训练参数中已设置自动保存策略无需手动保存。整个训练过程大概需要20 ~ 30 分钟完成后会在output目录下生成 LoRA 模型。显存参考本案例的 LLaMA3.1 基座模型为 8B 参数配合 bfloat16 半精度与梯度检查点24G 显存的 3090 级别机器即可胜任在 self-llm 的 LLaMA3 Lora 微调教程 中同样以 24G 显存机器为推荐环境。Step 4推理测试加载 LoRA 权重与嬛嬛对话训练完成后使用以下代码加载基座模型与 LoRA 权重进行对话测试from transformers import AutoModelForCausalLM, AutoTokenizer import torch from peft import PeftModel mode_path ./LLM-Research/Meta-Llama-3___1-8B-Instruct lora_path ./output/llama3_1_instruct_lora/checkpoint-699 # 这里改称你的 lora 输出对应 checkpoint 地址 # 加载tokenizer tokenizer AutoTokenizer.from_pretrained(mode_path, trust_remote_codeTrue) # 加载模型 model AutoModelForCausalLM.from_pretrained(mode_path, device_mapauto,torch_dtypetorch.bfloat16, trust_remote_codeTrue).eval() # 加载lora权重 model PeftModel.from_pretrained(model, model_idlora_path) prompt 嬛嬛你怎么了朕替你打抱不平 messages [ {role: system, content: 假设你是皇帝身边的女人--甄嬛。}, {role: user, content: prompt} ] input_ids tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue) # print(input_ids) model_inputs tokenizer([input_ids], return_tensorspt).to(cuda) generated_ids model.generate(model_inputs.input_ids,max_new_tokens512) generated_ids [ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids) ] response tokenizer.batch_decode(generated_ids, skip_special_tokensTrue)[0] print(皇上, prompt) print(嬛嬛,response)推理链路要点加载顺序先用AutoModelForCausalLM加载基座模型bfloat16、device_mapauto、.eval()切换评估模式再用PeftModel.from_pretrained(model, model_idlora_path)注入 LoRA 权重lora_path指向训练输出的具体 checkpoint例如./output/llama3_1_instruct_lora/checkpoint-699请按实际保存步数替换apply_chat_template直接复用 LLaMA3.1 的 chat template 组装system/user消息add_generation_promptTrue会在末尾追加assistant引导符model.generate(max_new_tokens512)控制生成长度随后按输入长度切片、skip_special_tokensTrue解码得到纯文本回复。测试输出示例皇上 嬛嬛你怎么了朕替你打抱不平 嬛嬛 皇上臣妾不是故意的。至此一个可以流畅使用甄嬛语气对话的角色模型就训练并部署完成了。扩展换基座模型复现同一流程本案例以 LLaMA3.1-8B-Instruct 为基座完成了演示但整套方法论完全可迁移只需将 dataset/huanhuan.json或自制的角色数据集与 examples/Chat-嬛嬛/train.py 中的模型路径、对话模板替换为其他基座模型即可。self-llm 仓库中提供了大量同类 LoRA 微调教程可供参考例如LLaMA3-8B-Instruct Lora 微调同样使用本案例的huanhuan.json数据集讲解指令集构建、数据格式化与训练全流程LLaMA3.1-8B-Instruct Lora 微调与本案例基座一致从环境配置到 LoRA 权重推理的完整参考。值得注意的是不同基座模型的 Chat Template 特殊 token 不同如 Qwen 系列使用|im_start|/|im_end|LLaMA3 系列使用|start_header_id|/|eot_id|替换基座时需要同步修改process_func中的模板拼接这也是将 train.py 迁移到其他模型时最需要关注的改动点。写在最后Chat-嬛嬛 诞生于大模型浪潮初期项目团队抱着如果不做点什么可能会错过很多有趣的事情的想法与几位小伙伴一起完成了从剧本数据清洗、角色对话抽取、LoRA 微调到对话验证的完整闭环。整个过程既验证了小说/剧本 → 角色 AI这一个性化大模型路径的可行性也为后来者提供了一份可以照抄的实战模板。如果你也有一部心仪的小说或剧本不妨按照本文的四步流程打造一个属于自己的角色 AI。本案例的主要贡献者Datawhale 成员宋志学中国矿业大学(北京)、邹雨衡对外经济贸易大学、王熠明宁夏大学、邓宇文广州大学、杜森南阳理工学院、肖鸿儒同济大学。【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/datawhalechina/self-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考