LoRA微调环境配置与实战指南
因为原来那个微调环境,在我和'deekseep'的操作下被搞崩了,由于现在的条件限制,我们现在也用不了那个jupyter的可视化界面,那么接下来的微调操作就不需要这个东西了。下面我们来重新配置微调环境。后面我会把微调过程也写出来。
经过第一次的配置,发现微调环境有很多库,不同的模型适配不同的库,不同的库又要和其他的库进行配合,一旦其中一个出了冲突,就会有连锁反应,所以配置的时候一定要小心。最好有个强力的AI来指引。
关于目录结构
先把目录结构再次整理一下,目录结构太乱会影响心情。

logs:记录日志
merged:保存lora权重和原模型合并过的模型
ckpts:保存LoRa权重
code:保存运行代码
data:保存训练数据集
models:保存原始模型
tmp:一些临时文件保存
微调环境创建*
然后我准备创建两个环境。一个用来微调,一个用来调用和训练。分成两个防止出冲突。
#创建conda环境
conda create -n zy_unsloth python=3.10 -y
conda activate zy_unsloth
#升级基础工具
python -m pip install -U pip setuptools wheel#这里升级了pip,setuptools,wheel
pip:用来安装包,旧版的pip不支持最新的.whl,解析复杂的依赖逻辑容易冲突
setuptools:python中负责调用编译器将其转化为可运行的状态。
wheel:成品仓库,.whl文件是编译好的成品,解压直接用,不需要在本地寻找C++编译器。
#升级PyTorch
它是微调的引擎,负责执行数学运算,管理显存等
原因:
1,硬件兼容显卡驱动CUDA,
2,新版可能引入新算法
3,性能优化
这里要从官网上面下载防止冲突。
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
#下载unsloth和训练依赖
pip install unsloth
我这里仅仅装了个unsloth,它就把其他的核心包给我装好了。以下代码完全没有执行的必要。
不要随意安装,如果一个包安装后出现爆红的现象,说明是环境出现冲突,检测微调环境是否可以继续使用,要马上找方法解决。解决不了就只能重装了。
pip install "transformers>=4.37.0" datasets accelerate peft trl sentencepiece
pip install bitsandbytes
pip install scikit-learn pandas tqdm
python - <<'PY'
import torch
import unsloth
import transformers
import bitsandbytes as bnb
print("torch:", torch.__version__)
print("cuda:", torch.cuda.is_available())
print("transformers:", transformers.__version__)
print("unsloth ok")
print("bitsandbytes ok")
PY
在终端执行,验证环境是否装好了,如果环境已经安装完成会有以下回显

如果有这些显示,说明必要的包已经装好了,并且导入成功。
冻结环境
有必要给环境冻结一下,给自己炼制一个悔蛊。
pip freeze > /workspace/weitiao/zy/logs/zy_unsloth_requirements.txt
以后想要这个环境直接创建一个环境然后执行:
pip install -r /workspace/weitiao/zy/logs/zy_unsloth_requirements.txt
可以得到一个一模一样的环境。
微调环境测试
然后我们来测试一下,是否可以跑通在这个微调环境下。
这里用个简单的数据集,测试一下
import os
from datasets import load_dataset
from unsloth import FastLanguageModel,is_bfloat16_supported
from transformers import TrainingArguments
from trl import SFTTrainer
BASE_MODEL = "/workspace/weitiao/zy/models/Qwen2.5-7B-Instruct"
TRAIN_FILE = "/workspace/weitiao/zy/data/train.jsonl"
VAL_FILE = "/workspace/weitiao/zy/data/val.jsonl"
OUTPUT_DIR = "/workspace/weitiao/zy/ckpts/qwen25_cyber_lora"
MAX_SEQ_LENGTH = 2048
LOAD_IN_4BIT = True
def format_example(example):
instruction = example.get("instruction", "").strip()
input_text = example.get("input", "").strip()
output = example.get("output", "").strip()
if input_text:
text = (
"<|im_start|>system\n"
"你是一个专业的网络安全助手,回答应准确、清晰、审慎。\n"
"<|im_end|>\n"
"<|im_start|>user\n"
f"{instruction}\n\n补充信息:{input_text}\n"
"<|im_end|>\n"
"<|im_start|>assistant\n"
f"{output}"
"<|im_end|>"
)
else:
text = (
"<|im_start|>system\n"
"你是一个专业的网络安全助手,回答应准确、清晰、审慎。\n"
"<|im_end|>\n"
"<|im_start|>user\n"
f"{instruction}\n"
"<|im_end|>\n"
"<|im_start|>assistant\n"
f"{output}"
"<|im_end|>"
)
return {"text": text}
def main():
os.makedirs(OUTPUT_DIR, exist_ok=True)
dataset = load_dataset(
"json",
data_files={"train": TRAIN_FILE, "validation": VAL_FILE}
)
dataset = dataset.map(format_example)
model, tokenizer = FastLanguageModel.from_pretrained(
model_name=BASE_MODEL,
max_seq_length=MAX_SEQ_LENGTH,
dtype=None,
load_in_4bit=LOAD_IN_4BIT,
)
tokenizer.padding_side = "right"
model = FastLanguageModel.get_peft_model(
model,
r=16,
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
],
lora_alpha=32,
lora_dropout=0,
bias="none",
use_gradient_checkpointing="unsloth",
random_state=3407,
use_rslora=False,
loftq_config=None,
)
training_args = TrainingArguments(
output_dir=OUTPUT_DIR,
per_device_train_batch_size=1,
gradient_accumulation_steps=8,
warmup_steps=10,
num_train_epochs=3,
learning_rate=2e-4,
fp16 = not is_bfloat16_supported(),
bf16 = is_bfloat16_supported(),
logging_steps=5,
optim="adamw_8bit",
weight_decay=0.01,
lr_scheduler_type="cosine",
seed=3407,
save_steps=50,
eval_strategy="steps",
eval_steps=50,
save_total_limit=2,
load_best_model_at_end=False,
report_to="none",
)
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset["train"],
eval_dataset=dataset["validation"],
dataset_text_field="text",
max_seq_length=MAX_SEQ_LENGTH,
packing=False,
args=training_args,
)
trainer.train()
model.save_pretrained(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)
print(f"LoRA adapter saved to: {OUTPUT_DIR}")
if __name__ == "__main__":
main()

这个脚本是可以跑通的,说明环境没有问题。
脚本都干了什么
这个脚本是AI写的以至于我现在完全不知道它引入了什么东西,利用什么参数,虽然是个测试脚本,我们就以它为例来学习一下。
下面我要讲一下,我用这个脚本都做了什么。
BASE_MODEL = "/workspace/weitiao/zy/models/Qwen2.5-7B-Instruct" # 原始大脑 LOAD_IN_4BIT = True # 压缩模式
#由于原版模型需要15G的显存才能正常运行,现在给压缩到了5G左右的显存可以正常运行,然后有更多剩余显存才更有余力往里面塞东西。
#LoRA参数的引入。
model = FastLanguageModel.get_peft_model(
model, r = 16, # 参数 A:学习空间
lora_alpha = 32, # 参数 B:影响力
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],# 参数 C:作用部位
lora_dropout = 0,
bias = "none", )
核心公式:
在大模型内部,每一个回答都是通过巨大的矩阵乘法计算出来的。
原始状态(被LoRA冻结): 模型原本的知识叫 W。当你问它问题时,它运行 Wx(x$ 是你的问题),得到一个普通回答。
LoRA 工作状态: 你引入了两个小矩阵 A 和 B。现在的计算变成了:
改变回答方向的原理:
r=16,就是定义了矩阵A和矩阵B的大小
A负责把问题x压缩到一个16维的小空间
B负责把这个16维的信息还原回模型的回答中
训练的过程就是不断替代A和B中的数值,使得BxA计算出来的结果,刚好抵消掉原模型的原始不必要回答,替换为你想要的安全专家回答
参数如何控制回答方向
a.修改r(Rank)--决定回答指向的细腻程度
如果r=4:这个小空间就太窄了,模型只能学会一些简单的关键词。回答问题也不会太标准。
如果r=16:空间足够大,模型不仅可以学会关键词,还可以学会网络安全的句式逻辑和语气。
如果r=1024:空间过大,模型会试图记住训练集中的每一句话,导致它失去了灵活变通能力
b.修改lora_alpha决定了回答指向的偏执程度
脚本中lora_alpha=32。它在数学上是一个系数,
逻辑:如果是
的两倍,相当于给新知识加了两倍的扩音器
效果:即使原模型想说一句普通的话,由于LoRA这边的信号放大了两倍,最终回答会被强制扭转到数据集方向。
C. 修改target_models --决定了指向的“覆盖范围”
脚本中写了:q_proj,v_proj,up_proj等等
q,k,v(注意力):修改这里,是让模型改变看问题的重点。比如看到"注入"这个词,普通模型关注的是"注进液体",微调后的模型会把注意力全部集中在"SQL注入"上。
up,down,gate(推理层/MLP):修改这里,让模型改变"思考的逻辑",它决定了模型如何把提取的重点组合成逻辑严密的回答。
是让大模型改变思考的逻辑,它决定了模型如何把提取到的重点组合成逻辑严密的回答。
为什么模型回答可以被指向其他地方
微调本质是在做“概率修正”,
(1)原模型:看到“如何看待注入”,它预测概率最高可能是:“医疗”“美容”方面
(2)微调成功过程中:当它预测到“医疗”时,脚本中的SFTTrainer发现与数据集中的"SQL注入"不符,就会产生一个误差信号。
(3)参数更新:这个误差信号传回给A和B矩阵
(4)最终指向:经过多次训练,A和B形成的就像一个导航修正。现在模型看到“注入”词汇,
想往医疗方向跑,但是
会产生强大拉力把它拉回到网络安全方面
教它说话(Prompt 模板)
text = (
"<|im_start|>system\n你是一个专业的网络安全助手...<|im_end|>\n"
"<|im_start|>user\n{instruction}\n<|im_end|>\n"
"<|im_start|>assistant\n{output}<|im_end|>"
)
强制模型学会这种带有标签的回答格式。ChatML 格式
意义:通过不断看这种带有<|im_start|>的对话,模型学会了:看到“system”我就要进入专家角色,看到“user”我就要准备回答。这改变了模型的对话习惯。
训练参数控制
learning_rate = 2e-4, # 学习率:讲课速度
num_train_epochs = 3, # 课时:书看三遍
optim = "adamw_8bit", # 优化器:学习方法
2e-4 (0.0002):这是一个稳健的学习速度,既能学会新知识,又不会让模型产生“幻觉”或者忘记原来的基本语言能力。
3Epochs :让模型把train.jsonl看三遍,形成记忆。
8-bit优化器:学习中减少思考成本。
微调环境就先到这。后面我会搞一个调用测试环境并且用大一点的数据集去微调,查看模型受到的影响。并进行压力测试。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)