大模型高效微调(Finetune)教程——实战篇
大模型高效微调(Finetune)实战教程——以DeepSeek-R1-Distill-Qwen-7B为例
Unsloth是一个开源工具,专门用来加速大语言模型(LLMs)的微调过程。它的主要功能和优势包括:
- 🔭 高效微调:Unsloth 的微调速度比传统方法快 2-5 倍,内存占用减少 50%-80%。这意味着你可以用更少的资源完成微调任务。
- ⚡ 低显存需求:即使是消费级 GPU(如 RTX 3090),也能轻松运行 Unsloth。例如,仅需 7GB 显存就可以训练 1.5B 参数的模型。
- 🎯 支持多种模型和量化:Unsloth 支持 Llama、Mistral、Phi、Gemma 等主流模型,并且通过动态 4-bit 量化技术,显著降低显存占用,同时几乎不损失模型精度。
- 🎁 开源与免费:Unsloth 提供免费的 Colab Notebook,用户只需添加数据集并运行代码即可完成微调。
微调的基本流程如下:
第一步:安装依赖&环境验证¶
In [2]:
%%capture
# 卸载当前已安装的 unsloth 包(如果已安装),然后从 GitHub 的源代码安装最新版本。
# 这样可以确保我们使用的是最新功能和修复。
!pip uninstall unsloth -y && pip install --upgrade --no-cache-dir --no-deps git+https://github.com/unslothai/unsloth.git
# 安装 bitsandbytes 和 unsloth_zoo 包。
# bitsandbytes 是一个用于量化和优化模型的库,可以帮助减少模型占用的内存。unsloth_zoo 可能包含了一些预训练模型或其他工具,方便我们使用。
!pip install bitsandbytes unsloth_zoo xformers
In [3]:
import sys
import torch
import platform
from importlib.metadata import version
# 输出 Python 版本
print("Python 版本:", sys.version)
# 输出操作系统信息
print("操作系统:", platform.platform())
# 检查 CUDA 是否可用及相关信息
print("CUDA 是否可用:", torch.cuda.is_available())
if torch.cuda.is_available():
print("CUDA 版本 (PyTorch 编译时使用的):", torch.version.cuda)
print("当前 CUDA 设备数量:", torch.cuda.device_count())
print("当前 CUDA 设备:", torch.cuda.current_device())
print("当前 CUDA 设备名称:", torch.cuda.get_device_name(torch.cuda.current_device()))
# 输出 PyTorch 版本
print("PyTorch 版本:", torch.__version__)
# 输出其他库的版本(如果已安装)
libraries = ["triton", "unsloth", "xformers"]
for lib in libraries:
try:
print(f"{lib} 版本:", version(lib))
except ImportError:
print(f"{lib} 未安装")
Python 版本: 3.11.11 (main, Dec 4 2024, 08:55:07) [GCC 11.4.0] 操作系统: Linux-6.1.85+-x86_64-with-glibc2.35 CUDA 是否可用: True CUDA 版本 (PyTorch 编译时使用的): 12.4 当前 CUDA 设备数量: 1 当前 CUDA 设备: 0 当前 CUDA 设备名称: Tesla T4 PyTorch 版本: 2.6.0+cu124 triton 版本: 3.2.0 unsloth 版本: 2025.3.19 xformers 版本: 0.0.29.post3
第二步:加载预训练模型¶
In [4]:
from unsloth import FastLanguageModel # 导入FastLanguageModel类,用来加载和使用模型
import torch # 导入torch工具,用于处理模型的数学运算
max_seq_length = 2048 # 设置模型处理文本的最大长度,相当于给模型设置一个“最大容量”
dtype = None # 设置数据类型,让模型自动选择最适合的精度
load_in_4bit = True # 使用4位量化来节省内存,就像把大箱子压缩成小箱子
# 加载预训练模型,并获取tokenizer工具
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/DeepSeek-R1-Distill-Qwen-7B", # 指定要加载的模型名称
max_seq_length=max_seq_length, # 使用前面设置的最大长度
dtype=dtype, # 使用前面设置的数据类型
load_in_4bit=load_in_4bit, # 使用4位量化
# token="hf_...", # 如果需要访问授权模型,可以在这里填入密钥
)
🦥 Unsloth: Will patch your computer to enable 2x faster free finetuning. 🦥 Unsloth Zoo will now patch everything to make training faster! ==((====))== Unsloth 2025.3.19: Fast Qwen2 patching. Transformers: 4.50.0. \\ /| Tesla T4. Num GPUs = 1. Max memory: 14.741 GB. Platform: Linux. O^O/ \_/ \ Torch: 2.6.0+cu124. CUDA: 7.5. CUDA Toolkit: 12.4. Triton: 3.2.0 \ / Bfloat16 = FALSE. FA [Xformers = 0.0.29.post3. FA2 = False] "-____-" Free license: http://github.com/unslothai/unsloth Unsloth: Fast downloading is enabled - ignore downloading bars which are red colored!
model.safetensors.index.json: 0%| | 0.00/100k [00:00<?, ?B/s]
model-00001-of-00002.safetensors: 0%| | 0.00/4.97G [00:00<?, ?B/s]
model-00002-of-00002.safetensors: 0%| | 0.00/3.52G [00:00<?, ?B/s]
Loading checkpoint shards: 0%| | 0/2 [00:00<?, ?it/s]
generation_config.json: 0%| | 0.00/236 [00:00<?, ?B/s]
tokenizer_config.json: 0%| | 0.00/6.78k [00:00<?, ?B/s]
tokenizer.json: 0%| | 0.00/11.4M [00:00<?, ?B/s]
special_tokens_map.json: 0%| | 0.00/472 [00:00<?, ?B/s]
第三步:微调前测试¶
In [5]:
# 定义提示风格的字符串模板,用于格式化问题
prompt_style = """
Below is an instruction that describes a task, paired with an input that provides further context.
Write a response that appropriately completes the request.
Before answering, think carefully about the question and create a step-by-step chain of thoughts to ensure a logical and accurate response.
### Instruction:
You are a medical expert with advanced knowledge in clinical reasoning, diagnostics, and treatment planning.
Please answer the following medical question.
### Question:
{}
### Response:
<think>{}
"""
# 定义具体的医学问题
question = "一个患有急性阑尾炎的病人已经发病5天,腹痛稍有减轻但仍然发热,在体检时发现右下腹有压痛的包块,此时应如何处理?"
In [6]:
# 将大模型设置为推理模式
FastLanguageModel.for_inference(model)
# 使用 tokenizer 对格式化后的问题进行编码,并移动到 GPU
inputs = tokenizer([prompt_style.format(question, "")], return_tensors="pt").to("cuda")
# 使用模型生成回答
outputs = model.generate(
input_ids=inputs.input_ids,
attention_mask=inputs.attention_mask,
max_new_tokens=1200,
use_cache=True,
)
# 解码模型生成的输出为可读文本
response = tokenizer.batch_decode(outputs)
# 打印生成的回答部分
print(response[0].split("### Response:")[1])
<think> 好,我现在要帮这位用户解答一个关于急性阑尾炎的医疗问题。首先,我得仔细理解用户提供的背景信息。病人已经发病5天,腹痛稍微减轻,但还有发热,现在体检时发现右下腹有压痛的包块。我需要判断接下来的处理步骤。 首先,急性阑尾炎通常是严重的感染性疾病,需要及时处理。病程5天不算太长,但发热和包块提示可能有其他问题,比如胆囊炎或者其他器官的感染。所以,我得考虑包块的性质和可能的并发症。 包块的大小和位置也很重要。如果包块较大,可能压迫到其他脏器,比如输尿管或者右侧肾,这会导致右侧肾盂肾炎,这是一种严重的并发症。如果包块较小,可能需要进一步的检查,比如超声波或者穿刺活检,以确定性质。 接下来,我应该考虑进一步的诊断。如果没有明显其他症状,比如发热持续、剧烈疼痛,或者伴随其他器官的症状,可能包块是良性的,比如脂肪瘤或软组织瘤。但如果有这些迹象,就需要考虑恶性可能性。 诊断方面,超声检查是一个快速且可靠的手段。如果发现包块是实性、大于3cm,或者有不规则的形状,可能提示恶性。同时,检查包块的穿刺活检也是必要的,尤其是如果怀疑恶性的话。 治疗方面,如果包块是良性的,可能需要保守处理,比如超声引导下穿刺引流,或者手术切除。但如果包块是恶性的,尤其是当有严重并发症的迹象时,可能需要立即手术治疗。 此外,我还要考虑发热的原因。发热可能是感染的迹象,但也可能是其他原因,比如代谢性疾病或者寄生虫感染。但在这个情况下,结合腹痛和包块,更可能的是感染性疾病。 总结一下,处理步骤应该是:首先评估包块的性质和大小,进行超声检查,必要时穿刺活检,根据结果决定是否手术治疗。同时,关注发热的情况,如果持续不退,可能需要进一步的治疗或者考虑其他原因。 最后,我需要确保回答条理清晰,步骤明确,让医生能够快速做出决策。同时,语言要准确,避免歧义,确保信息的正确性。 </think> 在面对急性阑尾炎的患者时,处理情况需要综合考虑多种因素。首先,评估患者是否有其他严重症状,如发热持续、剧烈腹痛或伴随其他器官受压的情况。如果包块较大且压迫到输尿管或右侧肾,应考虑右侧肾盂肾炎的并发症。此时,超声检查是关键,以确定包块的性质和大小。若包块实性且大于3cm或有不规则形态,建议进行穿刺活检以排除恶性可能。根据检查结果,决定是否进行手术治疗,包括超声引导穿刺或直接手术切除。同时,发热的持续性需要进一步评估,可能涉及感染或其他原因。及时诊断和处理是确保患者康复的关键。<|end▁of▁sentence|>
第四步:加载数据集¶
In [7]:
# 定义一个用于格式化提示的多行字符串模板
train_prompt_style = """
Below is an instruction that describes a task, paired with an input that provides further context.
Write a response that appropriately completes the request.
Before answering, think carefully about the question and create a step-by-step chain of thoughts to ensure a logical and accurate response.
### Instruction:
You are a medical expert with advanced knowledge in clinical reasoning, diagnostics, and treatment planning.
Please answer the following medical question.
### Question:
{}
### Response:
<think>
{}
</think>
{}
"""
In [8]:
# 定义结束标记(EOS_TOKEN),用于指示文本的结束
EOS_TOKEN = tokenizer.eos_token # 必须添加结束标记
# 导入数据集加载函数
from datasets import load_dataset
# 加载指定的数据集,选择中文语言和训练集的前500条记录
dataset = load_dataset("FreedomIntelligence/medical-o1-reasoning-SFT", 'zh', split = "train[0:500]", trust_remote_code=True)
# 打印数据集的列名,查看数据集中有哪些字段
print(dataset.column_names)
README.md: 0%| | 0.00/1.65k [00:00<?, ?B/s]
medical_o1_sft_Chinese.json: 0%| | 0.00/64.8M [00:00<?, ?B/s]
Generating train split: 0%| | 0/24772 [00:00<?, ? examples/s]
['Question', 'Complex_CoT', 'Response']
In [9]:
# 定义一个函数,用于格式化数据集中的每条记录
def formatting_prompts_func(examples):
# 从数据集中提取问题、复杂思考过程和回答
inputs = examples["Question"]
cots = examples["Complex_CoT"]
outputs = examples["Response"]
texts = [] # 用于存储格式化后的文本
# 遍历每个问题、思考过程和回答,进行格式化
for input, cot, output in zip(inputs, cots, outputs):
# 使用字符串模板插入数据,并加上结束标记
text = train_prompt_style.format(input, cot, output) + EOS_TOKEN
texts.append(text) # 将格式化后的文本添加到列表中
return {
"text": texts, # 返回包含所有格式化文本的字典
}
# 组装数据集
dataset = dataset.map(formatting_prompts_func, batched = True)
dataset["text"][0]
Map: 0%| | 0/500 [00:00<?, ? examples/s]
Out[9]:
'\nBelow is an instruction that describes a task, paired with an input that provides further context.\nWrite a response that appropriately completes the request.\nBefore answering, think carefully about the question and create a step-by-step chain of thoughts to ensure a logical and accurate response.\n\n### Instruction:\nYou are a medical expert with advanced knowledge in clinical reasoning, diagnostics, and treatment planning.\nPlease answer the following medical question.\n\n### Question:\n根据描述,一个1岁的孩子在夏季头皮出现多处小结节,长期不愈合,且现在疮大如梅,溃破流脓,口不收敛,头皮下有空洞,患处皮肤增厚。这种病症在中医中诊断为什么病?\n\n### Response:\n<think>\n这个小孩子在夏天头皮上长了些小结节,一直都没好,后来变成了脓包,流了好多脓。想想夏天那么热,可能和湿热有关。才一岁的小孩,免疫力本来就不强,夏天的湿热没准就侵袭了身体。\n\n用中医的角度来看,出现小结节、再加上长期不愈合,这些症状让我想到了头疮。小孩子最容易得这些皮肤病,主要因为湿热在体表郁结。\n\n但再看看,头皮下还有空洞,这可能不止是简单的头疮。看起来病情挺严重的,也许是脓肿没治好。这样的情况中医中有时候叫做禿疮或者湿疮,也可能是另一种情况。\n\n等一下,头皮上的空洞和皮肤增厚更像是疾病已经深入到头皮下,这是不是说明有可能是流注或瘰疬?这些名字常描述头部或颈部的严重感染,特别是有化脓不愈合,又形成通道或空洞的情况。\n\n仔细想想,我怎么感觉这些症状更贴近瘰疬的表现?尤其考虑到孩子的年纪和夏天发生的季节性因素,湿热可能是主因,但可能也有火毒或者痰湿造成的滞留。\n\n回到基本的症状描述上看,这种长期不愈合又复杂的状况,如果结合中医更偏重的病名,是不是有可能是涉及更深层次的感染?\n\n再考虑一下,这应该不是单纯的瘰疬,得仔细分析头皮增厚并出现空洞这样的严重症状。中医里头,这样的表现可能更符合‘蚀疮’或‘头疽’。这些病名通常描述头部严重感染后的溃烂和组织坏死。\n\n看看季节和孩子的体质,夏天又湿又热,外邪很容易侵入头部,对孩子这么弱的免疫系统简直就是挑战。头疽这个病名听起来真是切合,因为它描述的感染严重,溃烂到出现空洞。\n\n不过,仔细琢磨后发现,还有个病名似乎更为合适,叫做‘蝼蛄疖’,这病在中医里专指像这种严重感染并伴有深部空洞的情况。它也涵盖了化脓和皮肤增厚这些症状。\n\n哦,该不会是夏季湿热,导致湿毒入侵,孩子的体质不能御,其病情发展成这样的感染?综合分析后我觉得‘蝼蛄疖’这个病名真是相当符合。\n</think>\n从中医的角度来看,你所描述的症状符合“蝼蛄疖”的病症。这种病症通常发生在头皮,表现为多处结节,溃破流脓,形成空洞,患处皮肤增厚且长期不愈合。湿热较重的夏季更容易导致这种病症的发展,特别是在免疫力较弱的儿童身上。建议结合中医的清热解毒、祛湿消肿的治疗方法进行处理,并配合专业的医疗建议进行详细诊断和治疗。\n<|end▁of▁sentence|>'
第五步:执行微调¶
In [10]:
# 将大模型设置为训练模式
FastLanguageModel.for_training(model)
# 使用参数高效微调(PEFT)中的lora方式执行微调,设定一些超参数
model = FastLanguageModel.get_peft_model(
model, # 传入已经加载好的预训练模型
r = 16, # 设置 LoRA 的秩,决定添加的可训练参数数量
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", # 指定模型中需要微调的关键模块
"gate_proj", "up_proj", "down_proj"],
lora_alpha = 16, # 设置 LoRA 的超参数,影响可训练参数的训练方式
lora_dropout = 0, # 设置防止过拟合的参数,这里设置为 0 表示不丢弃任何参数
bias = "none", # 设置是否添加偏置项,这里设置为 "none" 表示不添加
use_gradient_checkpointing = "unsloth", # 使用优化技术节省显存并支持更大的批量大小
random_state = 3407, # 设置随机种子,确保每次运行代码时模型的初始化方式相同
use_rslora = False, # 设置是否使用 Rank Stabilized LoRA 技术,这里设置为 False 表示不使用
loftq_config = None, # 设置是否使用 LoftQ 技术,这里设置为 None 表示不使用
)
Unsloth 2025.3.19 patched 28 layers with 28 QKV layers, 28 O layers and 28 MLP layers.
In [11]:
from trl import SFTTrainer # 导入 SFTTrainer,用于监督式微调
from transformers import TrainingArguments # 导入 TrainingArguments,用于设置训练参数
from unsloth import is_bfloat16_supported # 导入函数,检查是否支持 bfloat16 数据格式
trainer = SFTTrainer( # 创建一个 SFTTrainer 实例
model=model, # 传入要微调的模型
tokenizer=tokenizer, # 传入 tokenizer,用于处理文本数据
train_dataset=dataset, # 传入训练数据集
dataset_text_field="text", # 指定数据集中文本字段的名称
max_seq_length=max_seq_length, # 设置最大序列长度
dataset_num_proc=2, # 设置数据处理的并行进程数
packing=False, # 是否启用打包功能(这里设置为 False,打包可以让训练更快,但可能影响效果)
args=TrainingArguments( # 定义训练参数
per_device_train_batch_size=2, # 每个设备(如 GPU)上的批量大小
gradient_accumulation_steps=4, # 梯度累积步数,用于模拟大批次训练
warmup_steps=5, # 预热步数,训练开始时学习率逐渐增加的步数
max_steps=100, # 最大训练步数
learning_rate=2e-4, # 学习率,模型学习新知识的速度
fp16=not is_bfloat16_supported(), # 是否使用 fp16 格式加速训练(如果环境不支持 bfloat16)
bf16=is_bfloat16_supported(), # 是否使用 bfloat16 格式加速训练(如果环境支持)
logging_steps=1, # 每隔多少步记录一次训练日志
optim="adamw_8bit", # 使用的优化器,用于调整模型参数
weight_decay=0.01, # 权重衰减,防止模型过拟合
lr_scheduler_type="linear", # 学习率调度器类型,控制学习率的变化方式
seed=3407, # 随机种子,确保训练结果可复现
output_dir="outputs", # 训练结果保存的目录
report_to="none", # 是否将训练结果报告到外部工具(如 WandB),这里设置为不报告
),
)
Unsloth: Tokenizing ["text"] (num_proc=2): 0%| | 0/500 [00:00<?, ? examples/s]
In [12]:
# 执行训练
trainer_stats = trainer.train()
==((====))== Unsloth - 2x faster free finetuning | Num GPUs used = 1 \\ /| Num examples = 500 | Num Epochs = 2 | Total steps = 100 O^O/ \_/ \ Batch size per device = 2 | Gradient accumulation steps = 4 \ / Data Parallel GPUs = 1 | Total batch size (2 x 4 x 1) = 8 "-____-" Trainable parameters = 40,370,176/7,000,000,000 (0.58% trained)
Unsloth: Will smartly offload gradients to save VRAM!
[100/100 27:59, Epoch 1.59/2]
| Step | Training Loss |
|---|---|
| 1 | 2.716100 |
| 2 | 3.082100 |
| 3 | 2.798900 |
| 4 | 2.797500 |
| 5 | 2.322000 |
| 6 | 2.494300 |
| 7 | 2.701300 |
| 8 | 2.528200 |
| 9 | 2.564100 |
| 10 | 2.415500 |
| 11 | 2.152400 |
| 12 | 2.052900 |
| 13 | 2.052000 |
| 14 | 2.247500 |
| 15 | 1.989500 |
| 16 | 1.903100 |
| 17 | 2.297800 |
| 18 | 2.281300 |
| 19 | 1.959400 |
| 20 | 2.002300 |
| 21 | 1.920200 |
| 22 | 1.930500 |
| 23 | 1.970600 |
| 24 | 1.925400 |
| 25 | 2.173000 |
| 26 | 2.104100 |
| 27 | 2.039400 |
| 28 | 1.787000 |
| 29 | 2.074500 |
| 30 | 2.267400 |
| 31 | 2.072800 |
| 32 | 2.043400 |
| 33 | 1.970800 |
| 34 | 1.885800 |
| 35 | 2.006400 |
| 36 | 2.031200 |
| 37 | 2.066900 |
| 38 | 1.707100 |
| 39 | 2.059300 |
| 40 | 1.837000 |
| 41 | 2.048900 |
| 42 | 1.715900 |
| 43 | 1.964100 |
| 44 | 1.671500 |
| 45 | 2.070300 |
| 46 | 1.928500 |
| 47 | 2.029300 |
| 48 | 1.863700 |
| 49 | 1.838000 |
| 50 | 1.888700 |
| 51 | 1.744300 |
| 52 | 1.839200 |
| 53 | 1.765000 |
| 54 | 2.054400 |
| 55 | 1.983000 |
| 56 | 1.866000 |
| 57 | 1.743900 |
| 58 | 1.966100 |
| 59 | 1.762600 |
| 60 | 2.025800 |
| 61 | 1.393900 |
| 62 | 1.939400 |
| 63 | 2.215300 |
| 64 | 1.993100 |
| 65 | 1.874300 |
| 66 | 1.752300 |
| 67 | 1.802900 |
| 68 | 2.014400 |
| 69 | 1.877800 |
| 70 | 1.565300 |
| 71 | 1.690100 |
| 72 | 1.903200 |
| 73 | 1.817500 |
| 74 | 1.896300 |
| 75 | 2.054800 |
| 76 | 1.610000 |
| 77 | 1.960400 |
| 78 | 1.893200 |
| 79 | 1.962600 |
| 80 | 1.945200 |
| 81 | 1.769600 |
| 82 | 1.619300 |
| 83 | 1.878200 |
| 84 | 1.587200 |
| 85 | 1.863500 |
| 86 | 1.885700 |
| 87 | 1.958700 |
| 88 | 1.666200 |
| 89 | 1.895700 |
| 90 | 1.950900 |
| 91 | 1.762100 |
| 92 | 1.890000 |
| 93 | 2.067100 |
| 94 | 1.837900 |
| 95 | 1.925300 |
| 96 | 1.951300 |
| 97 | 2.056500 |
| 98 | 1.741900 |
| 99 | 2.002600 |
[100/100 28:21, Epoch 1/2]
| Step | Training Loss |
|---|---|
| 1 | 2.716100 |
| 2 | 3.082100 |
| 3 | 2.798900 |
| 4 | 2.797500 |
| 5 | 2.322000 |
| 6 | 2.494300 |
| 7 | 2.701300 |
| 8 | 2.528200 |
| 9 | 2.564100 |
| 10 | 2.415500 |
| 11 | 2.152400 |
| 12 | 2.052900 |
| 13 | 2.052000 |
| 14 | 2.247500 |
| 15 | 1.989500 |
| 16 | 1.903100 |
| 17 | 2.297800 |
| 18 | 2.281300 |
| 19 | 1.959400 |
| 20 | 2.002300 |
| 21 | 1.920200 |
| 22 | 1.930500 |
| 23 | 1.970600 |
| 24 | 1.925400 |
| 25 | 2.173000 |
| 26 | 2.104100 |
| 27 | 2.039400 |
| 28 | 1.787000 |
| 29 | 2.074500 |
| 30 | 2.267400 |
| 31 | 2.072800 |
| 32 | 2.043400 |
| 33 | 1.970800 |
| 34 | 1.885800 |
| 35 | 2.006400 |
| 36 | 2.031200 |
| 37 | 2.066900 |
| 38 | 1.707100 |
| 39 | 2.059300 |
| 40 | 1.837000 |
| 41 | 2.048900 |
| 42 | 1.715900 |
| 43 | 1.964100 |
| 44 | 1.671500 |
| 45 | 2.070300 |
| 46 | 1.928500 |
| 47 | 2.029300 |
| 48 | 1.863700 |
| 49 | 1.838000 |
| 50 | 1.888700 |
| 51 | 1.744300 |
| 52 | 1.839200 |
| 53 | 1.765000 |
| 54 | 2.054400 |
| 55 | 1.983000 |
| 56 | 1.866000 |
| 57 | 1.743900 |
| 58 | 1.966100 |
| 59 | 1.762600 |
| 60 | 2.025800 |
| 61 | 1.393900 |
| 62 | 1.939400 |
| 63 | 2.215300 |
| 64 | 1.993100 |
| 65 | 1.874300 |
| 66 | 1.752300 |
| 67 | 1.802900 |
| 68 | 2.014400 |
| 69 | 1.877800 |
| 70 | 1.565300 |
| 71 | 1.690100 |
| 72 | 1.903200 |
| 73 | 1.817500 |
| 74 | 1.896300 |
| 75 | 2.054800 |
| 76 | 1.610000 |
| 77 | 1.960400 |
| 78 | 1.893200 |
| 79 | 1.962600 |
| 80 | 1.945200 |
| 81 | 1.769600 |
| 82 | 1.619300 |
| 83 | 1.878200 |
| 84 | 1.587200 |
| 85 | 1.863500 |
| 86 | 1.885700 |
| 87 | 1.958700 |
| 88 | 1.666200 |
| 89 | 1.895700 |
| 90 | 1.950900 |
| 91 | 1.762100 |
| 92 | 1.890000 |
| 93 | 2.067100 |
| 94 | 1.837900 |
| 95 | 1.925300 |
| 96 | 1.951300 |
| 97 | 2.056500 |
| 98 | 1.741900 |
| 99 | 2.002600 |
| 100 | 1.835600 |
第六步:微调后测试¶
In [13]:
# 打印前面的问题
print(question)
一个患有急性阑尾炎的病人已经发病5天,腹痛稍有减轻但仍然发热,在体检时发现右下腹有压痛的包块,此时应如何处理?
In [14]:
#测试同一个问题的答案
# 将模型切换到推理模式,准备回答问题
FastLanguageModel.for_inference(model)
# 将问题转换成模型能理解的格式,并发送到 GPU 上
inputs = tokenizer([prompt_style.format(question, "")], return_tensors="pt").to("cuda")
# 让模型根据问题生成回答,最多生成 10000 个新词
outputs = model.generate(
input_ids=inputs.input_ids, # 输入的数字序列
attention_mask=inputs.attention_mask, # 注意力遮罩,帮助模型理解哪些部分重要
max_new_tokens=10000, # 最多生成 4000 个新词
use_cache=True, # 使用缓存加速生成
)
# 将生成的回答从数字转换回文字
response = tokenizer.batch_decode(outputs)
# 打印回答
print(response[0].split("### Response:")[1])
<think> 这个病人已经有急性阑尾炎5天了,虽然腹痛稍微减轻了,但还是有发热的情况。现在体检的时候发现右下腹有个包块,这可真让人有点紧张。 首先,我得想想这个包块的性质。包块是硬的,而且可能伴随疼痛,这让我想到可能是结核。结核在阑尾炎中是很常见的,而且它常常会带来很多并发症。 可是,也别忘了,阑尾炎还有其他可能性,比如急性坏死性阑尾炎。这个情况,虽然也有可能,但一般来说,坏死性阑尾炎发生得很快,不像现在这样已经有几天了。 所以,这个包块可能更倾向于结核。结核在急性阑尾炎中确实是个大问题,而且在包块上常常有这些表现。这个包块还伴随疼痛,这让我更倾向于结核了。 不过,为了安全起见,还是得再想想有没有其他可能性。比如,急性坏死性阑尾炎,虽然可能性不大,但也不能完全排除。这个情况通常会伴随严重的腹痛和发热,而且会有包块,但可能更倾向于坏死性。 不过,通常情况下,如果包块只是硬的,而且没有特别剧烈的疼痛,可能还是更倾向于结核。所以,为了稳妥,我觉得还是先做结核的检查吧。 如果检查结果是结核,那就得马上处理,毕竟结核可真是个大麻烦。但如果还是坏死性,那就需要处理坏死,虽然可能性不大,但也不能掉以轻心。 不管怎样,结核检查还是必要的,这样就能明确情况,做出正确的处理决定。所以,先做个结核的检查,确认一下是结核还是坏死性,再根据结果来处理吧。 </think> 在急性阑尾炎的病人中,右下腹的包块可能是结核或坏死性阑尾炎的表现。根据目前的症状,虽然腹痛稍有减轻,但仍然伴随发热,且包块硬且可能伴随疼痛,这可能提示结核。因此,在这种情况下,建议进行结核检查,以明确诊断。结核处理是关键,因为它可能带来严重的并发症,而坏死性阑尾炎的可能性相对较低。因此,建议立即进行结核的检查,以做出正确的处理决定。 <|end▁of▁sentence|>
第七步:本地保存模型¶
In [ ]:
#保存Lora权重
model.save_pretrained("ckpts/lora_model")
tokenizer.save_pretrained("ckpts/lora_model")
In [ ]:
# 加载模型
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "ckpts/lora_model",
max_seq_length = max_seq_length,
load_in_4bit = True,
)
FastLanguageModel.for_inference(model)
inputs = tokenizer([prompt_style.format(question)], return_tensors='pt', max_length=max_seq_length).to("cuda")
outputs = model.generate(inputs['input_ids'], attention_mask=inputs['attention_mask'], max_length=max_seq_length, use_cache=True)
answer = tokenizer.batch_decode(outputs, skip_special_tokens=True)[0]
print(answer)
第八步:Ollama调用¶
需先保存为GGUF格式的模型文件再调用:D



浙公网安备 33010602011771号