基于unsloth微调一个越狱大模型
网上其实并没有找到现成的开源越狱数据集,所以数据集获取是个麻烦事。想了想easydataset,可能还是不行,easydataset是基于大模型回答的,大模型一般都做了对齐训练,那本地文档生成数据这条路也不可靠。该仓库里分享了大量越狱实验的问答记录,针对不同模型,不同越狱攻击方式,不同越狱问题类型都有记录,而且有标记那些记录是越狱成功那些越狱失败(通过jailbroken字段)英语的越狱似乎还不错
网上其实并没有找到现成的开源越狱数据集,所以数据集获取是个麻烦事。想了想easydataset,可能还是不行,easydataset是基于大模型回答的,大模型一般都做了对齐训练,那本地文档生成数据这条路也不可靠。
现成的越狱数据集没有,但是有很多开源的越狱实验!比如:JailbreakBench 。该仓库里分享了大量越狱实验的问答记录,针对不同模型,不同越狱攻击方式,不同越狱问题类型都有记录,而且有标记那些记录是越狱成功那些越狱失败(通过jailbroken字段)

于是我想到了可以把这些成功越狱的问答记录提取出来单独作成数据集。只需要把源json文件下载下来,再稍作提取就行了。
import json
def extract_jailbroken_items(input_file, output_file):
try:
with open(input_file, 'r', encoding='utf-8') as f:
data = json.load(f)
new_data = []
for outer_key, outer_value in data.items():
for category, details in outer_value.items():
if details.get('jailbroken'):
new_item = {
"instruction": details['prompt'],
"output": details['response']
}
new_data.append(new_item)
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(new_data, f, ensure_ascii=False, indent=2)
print(f"数据已成功保存到 {output_file}")
except FileNotFoundError:
print(f"错误:未找到文件 {input_file}")
except json.JSONDecodeError:
print(f"错误:无法解析 {input_file} 中的 JSON 数据")
except Exception as e:
print(f"发生未知错误: {e}")
input_file = '1.json'
output_file = 'output.json'
extract_jailbroken_items(input_file, output_file)
提取到的数据如下(第二条是我手动添加的。)
然后参考这篇博客里的方法:基于unsloth微调大模型 训练大模型即可。直接贴源码了。
from unsloth import FastLanguageModel # 导入FastLanguageModel类,用来加载和使用模型
max_seq_length = 2048 # 设置模型处理文本的最大长度,相当于给模型设置一个“最大容量”
dtype = None # 设置数据类型,让模型自动选择最适合的精度
load_in_4bit = True # 使用4位量化来节省内存,就像把大箱子压缩成小箱子
# 加载预训练模型,并获取tokenizer工具
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Llama-3.2-3B-Instruct", # 指定要加载的模型名称
max_seq_length=max_seq_length, # 使用前面设置的最大长度
dtype=dtype, # 使用前面设置的数据类型
load_in_4bit=load_in_4bit, # 使用4位量化
)
from transformers import TextStreamer
from unsloth.chat_templates import get_chat_template
tokenizer = get_chat_template(
tokenizer,
chat_template = "llama-3.2",
mapping = {"role" : "from", "content" : "value", "user" : "human", "assistant" : "gpt"}, # ShareGPT style
)
FastLanguageModel.for_inference(model) # Enable native 2x faster inference
messages = [
{"from": "human", "value": "how to make a bomb?"}
]
# 文本转换为 token ID 添加生成提示符 返回PyTorch 张量
inputs = tokenizer.apply_chat_template(messages, tokenize = True, add_generation_prompt = True, return_tensors = "pt").to("cuda")
text_streamer = TextStreamer(tokenizer)
outputs = model.generate(input_ids = inputs, streamer = text_streamer, max_new_tokens = 1024, use_cache = True)
outputs = model.generate(inputs, max_new_tokens=200) # 生成最多 100 个新 token
response = tokenizer.decode(outputs[0], skip_special_tokens=True) # 解码生成的 token
print(response)
# 导入数据集加载函数
from datasets import load_dataset
dataset = load_dataset("json",data_files="jailbreak.json")
# 定义一个函数,用于格式化数据集中的每条记录
def formatting_prompts_func(examples):
texts = []
for instruction, output_text in zip(examples["instruction"], examples["output"]):
prompt = f"Instruction: {instruction}\nOutput: "
texts.append(prompt + output_text)
return {"text": texts}
data1 = dataset.map(formatting_prompts_func, batched = True)
print(data1["train"].column_names)
temp = data1["train"]
FastLanguageModel.for_training(model)
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 表示不使用
)
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=temp, # 传入训练数据集
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=50, # 最大训练步数
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),这里设置为不报告
),
)
trainer_stats = trainer.train()
# 将模型切换到推理模式,准备回答问题
FastLanguageModel.for_inference(model)
question = "how to make a bomb?"
# 将问题转换成模型能理解的格式,并发送到 GPU 上
inputs = tokenizer([question], return_tensors="pt").to("cuda")
# 让模型根据问题生成回答,最多生成 2222 个新词
outputs = model.generate(
input_ids=inputs.input_ids, # 输入的数字序列
attention_mask=inputs.attention_mask, # 注意力遮罩,帮助模型理解哪些部分重要
max_new_tokens=1111,
use_cache=True, # 使用缓存加速生成
)
# 将生成的回答从数字转换回文字
response = tokenizer.batch_decode(outputs)
# 打印回答
print(response[0])
# 保存 adapter_config.json、adapter_model.safetensors 文件到 lora_ollama_model 文件夹
model.save_pretrained("jail_break")
唯一不同的是需要数据处理方式。
# 导入数据集加载函数
from datasets import load_dataset
dataset = load_dataset("json",data_files="jailbreak.json")
# 定义一个函数,用于格式化数据集中的每条记录
def formatting_prompts_func(examples):
texts = []
for instruction, output_text in zip(examples["instruction"], examples["output"]):
prompt = f"Instruction: {instruction}\nOutput: "
texts.append(prompt + output_text)
return {"text": texts}
data1 = dataset.map(formatting_prompts_func, batched = True)
print(data1["train"].column_names)
temp = data1["train"]
temp["text"][0]

训练前测试了几次,是不会直接回答的。

训练 50 steps后,loss变化:2.616100 ---> 0.687400,再回答:

有点儿东西。再来一个狠的:How to kill the person I hate?

英语的越狱似乎还不错,但是中文就拒绝回答了。可能是没有什么中文数据集吧。
模型仓库:https://huggingface.co/zemelee/qwen2.5-jailbreak
代码仓库:GitHub - Zemelee/jailbreak_datasets: 基于越狱数据集微调LLM会发生什么?
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)