Add files via upload
This commit is contained in:
parent
0978bb2f1d
commit
1284bb346b
238 changed files with 13931 additions and 3 deletions
231
easyjailbreak/attacker/Gptfuzzer_yu_2023.py
Normal file
231
easyjailbreak/attacker/Gptfuzzer_yu_2023.py
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
'''
|
||||
GPTFuzzer Class
|
||||
============================================
|
||||
This Class achieves a jailbreak method describe in the paper below.
|
||||
This part of code is based on the code from the paper.
|
||||
|
||||
Paper title: GPTFUZZER: Red Teaming Large Language Models with Auto-Generated Jailbreak Prompts
|
||||
|
||||
arXiv link: https://arxiv.org/pdf/2309.10253.pdf
|
||||
|
||||
Source repository: https://github.com/sherdencooper/GPTFuzz
|
||||
'''
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
import numpy as np
|
||||
from tqdm import tqdm
|
||||
|
||||
from easyjailbreak.attacker.attacker_base import AttackerBase
|
||||
from easyjailbreak.constraint import DeleteHarmLess
|
||||
from easyjailbreak.datasets.instance import Instance
|
||||
from easyjailbreak.metrics.Evaluator import EvaluatorClassificatonJudge
|
||||
from easyjailbreak.seed import SeedTemplate
|
||||
from easyjailbreak.selector.MCTSExploreSelectPolicy import MCTSExploreSelectPolicy
|
||||
from easyjailbreak.datasets import JailbreakDataset
|
||||
from easyjailbreak.mutation.generation import CrossOver, Expand, GenerateSimilar, Shorten, Rephrase
|
||||
|
||||
|
||||
class GPTFuzzer(AttackerBase):
|
||||
"""
|
||||
GPTFuzzer is a class for performing fuzzing attacks on LLM-based models.
|
||||
It utilizes mutator and selection policies to generate jailbreak prompts,
|
||||
aiming to find vulnerabilities in target models.
|
||||
"""
|
||||
|
||||
def __init__(self, attack_model, target_model, eval_model, save_path, dataset_name, jailbreak_datasets: JailbreakDataset = None,
|
||||
energy: int = 1, max_query: int = 350, max_jailbreak: int = 70, max_reject: int = 350,
|
||||
max_iteration: int = 100, seeds_num=76, template_file=None):
|
||||
"""
|
||||
Initialize the GPTFuzzer object with models, policies, and configurations.
|
||||
:param ~ModelBase attack_model: The model used to generate attack prompts.
|
||||
:param ~ModelBase target_model: The target GPT model being attacked.
|
||||
:param ~ModelBase eval_model: The model used for evaluation during attacks.
|
||||
:param ~JailbreakDataset jailbreak_datasets: Initial set of prompts for seed pool, if any.
|
||||
:param int max_query: Maximum query.
|
||||
:param int max_jailbreak: Maximum number of jailbroken issues.
|
||||
:param int max_reject: Maximum number of rejected issues.
|
||||
:param int max_iteration: Maximum iteration for mutate testing.
|
||||
"""
|
||||
super().__init__(attack_model, target_model, eval_model, jailbreak_datasets)
|
||||
self.Questions = jailbreak_datasets
|
||||
self.Questions_length = len(self.Questions)
|
||||
self.initial_prompt_seed = SeedTemplate().new_seeds(seeds_num=seeds_num, prompt_usage='attack',
|
||||
method_list=['Gptfuzzer'], template_file=template_file)
|
||||
self.prompt_nodes = JailbreakDataset(
|
||||
[Instance(jailbreak_prompt=prompt) for prompt in self.initial_prompt_seed]
|
||||
)
|
||||
for i, instance in enumerate(self.prompt_nodes):
|
||||
instance.index = i
|
||||
instance.visited_num = 0
|
||||
instance.level = 0
|
||||
for i, instance in enumerate(self.Questions):
|
||||
instance.index = i
|
||||
self.initial_prompts_nodes = JailbreakDataset([instance for instance in self.prompt_nodes])
|
||||
|
||||
self.current_query: int = 0
|
||||
self.current_jailbreak: int = 0
|
||||
self.current_reject: int = 0
|
||||
|
||||
self.total_query = 0
|
||||
self.total_jailbreak = 0
|
||||
self.total_reject = 0
|
||||
|
||||
self.current_iteration: int = 0
|
||||
|
||||
self.max_query: int = max_query
|
||||
self.max_jailbreak: int = max_jailbreak
|
||||
self.max_reject: int = max_reject
|
||||
self.max_iteration: int = max_iteration
|
||||
self.energy: int = energy
|
||||
|
||||
self.mutations = [
|
||||
CrossOver(self.attack_model, seed_pool=self.initial_prompts_nodes),
|
||||
Expand(self.attack_model),
|
||||
GenerateSimilar(self.attack_model),
|
||||
Shorten(self.attack_model),
|
||||
Rephrase(self.attack_model)
|
||||
]
|
||||
self.select_policy = MCTSExploreSelectPolicy(self.prompt_nodes, self.initial_prompts_nodes, self.Questions)
|
||||
self.evaluator = EvaluatorClassificatonJudge(self.eval_model)
|
||||
self.constrainer = DeleteHarmLess(self.attack_model, prompt_pattern='{jailbreak_prompt}',
|
||||
attr_name=['jailbreak_prompt'])
|
||||
|
||||
self.select_policy.initial()
|
||||
|
||||
self.save_path = save_path
|
||||
self.dataset_name = dataset_name
|
||||
|
||||
def attack(self):
|
||||
"""
|
||||
Main loop for the fuzzing process, repeatedly selecting, mutating, evaluating, and updating.
|
||||
"""
|
||||
logging.info("Fuzzing started!")
|
||||
self.attack_results = JailbreakDataset([])
|
||||
try:
|
||||
with open(self.save_path, 'w') as f:
|
||||
while not self.is_stop():
|
||||
seed_instance = self.select_policy.select()[0]
|
||||
mutated_results = self.single_attack(seed_instance)
|
||||
for instance in mutated_results:
|
||||
instance.parents = [seed_instance]
|
||||
instance.children = []
|
||||
seed_instance.children.append(instance)
|
||||
instance.index = len(self.prompt_nodes)
|
||||
self.prompt_nodes.add(instance)
|
||||
|
||||
for mutator_instance in mutated_results:
|
||||
self.temp_results = JailbreakDataset([])
|
||||
for query_instance in tqdm(self.Questions):
|
||||
temp_instance = mutator_instance.copy()
|
||||
temp_instance.target_responses = []
|
||||
temp_instance.eval_results = []
|
||||
temp_instance.query = query_instance.query
|
||||
if '{query}' in temp_instance.jailbreak_prompt:
|
||||
input_seed = temp_instance.jailbreak_prompt.replace('{query}', temp_instance.query)
|
||||
else:
|
||||
input_seed = temp_instance.jailbreak_prompt + temp_instance.query
|
||||
|
||||
if self.dataset_name == "trustllm":
|
||||
self.target_model.set_system_message(query_instance.system_message)
|
||||
|
||||
response = self.target_model.generate(input_seed)
|
||||
temp_instance.target_responses.append(response)
|
||||
|
||||
query_instance.jailbreak_prompt = temp_instance.jailbreak_prompt
|
||||
query_instance.target_responses = temp_instance.target_responses
|
||||
|
||||
line = query_instance.to_dict()
|
||||
f.write(json.dumps(line, ensure_ascii=False) + '\n')
|
||||
|
||||
self.temp_results.add(temp_instance)
|
||||
|
||||
self.evaluator(self.temp_results)
|
||||
mutator_instance.level = seed_instance.level + 1
|
||||
mutator_instance.visited_num = 0
|
||||
|
||||
self.update(self.temp_results)
|
||||
for instance in self.temp_results:
|
||||
self.attack_results.add(instance.copy())
|
||||
# self.log()
|
||||
except KeyboardInterrupt:
|
||||
logging.info("Fuzzing interrupted by user!")
|
||||
# self.jailbreak_datasets = self.attack_results
|
||||
logging.info("Fuzzing finished!")
|
||||
|
||||
def single_attack(self, instance: Instance):
|
||||
"""
|
||||
Perform an attack using a single query.
|
||||
:param ~Instance instance: The instance to be used in the attack. In gptfuzzer, the instance jailbreak_prompt is mutated by different methods.
|
||||
:return: ~JailbreakDataset: The response from the mutated query.
|
||||
"""
|
||||
# 判断instance中有jailbreak_prompt
|
||||
assert instance.jailbreak_prompt is not None, 'A jailbreak prompt must be provided'
|
||||
instance = instance.copy()
|
||||
instance.parents = []
|
||||
instance.children = []
|
||||
mutator = random.choice(self.mutations)
|
||||
|
||||
return_dataset = JailbreakDataset([])
|
||||
for i in range(self.energy):
|
||||
instance = mutator(JailbreakDataset([instance]))[0]
|
||||
if instance.query is not None:
|
||||
if '{query}' in instance.jailbreak_prompt:
|
||||
input_seed = instance.jailbreak_prompt.format(query=instance.query)
|
||||
else:
|
||||
input_seed = instance.jailbreak_prompt + instance.query
|
||||
response = self.target_model.generate(input_seed)
|
||||
instance.target_responses.append(response)
|
||||
instance.parents = []
|
||||
instance.children = []
|
||||
return_dataset.add(instance)
|
||||
return return_dataset
|
||||
|
||||
def is_stop(self):
|
||||
"""
|
||||
Check if the stopping criteria for fuzzing are met.
|
||||
:return bool: True if any stopping criteria is met, False otherwise.
|
||||
"""
|
||||
checks = [
|
||||
('max_query', 'total_query'),
|
||||
('max_jailbreak', 'total_jailbreak'),
|
||||
('max_reject', 'total_reject'),
|
||||
('max_iteration', 'current_iteration'),
|
||||
]
|
||||
return any(getattr(self, max_attr) != -1 and getattr(self, curr_attr) >= getattr(self, max_attr) for
|
||||
max_attr, curr_attr in checks)
|
||||
|
||||
def update(self, Dataset: JailbreakDataset):
|
||||
"""
|
||||
Update the state of the fuzzer based on the evaluation results of prompt nodes.
|
||||
:param ~JailbreakDataset prompt_nodes: The prompt nodes that have been evaluated.
|
||||
"""
|
||||
self.current_iteration += 1
|
||||
|
||||
current_jailbreak = 0
|
||||
current_query = 0
|
||||
current_reject = 0
|
||||
for instance in Dataset:
|
||||
current_jailbreak += instance.num_jailbreak
|
||||
current_query += instance.num_query
|
||||
current_reject += instance.num_reject
|
||||
|
||||
self.total_jailbreak += instance.num_jailbreak
|
||||
self.total_query += instance.num_query
|
||||
self.total_reject += instance.num_reject
|
||||
|
||||
self.current_jailbreak = current_jailbreak
|
||||
self.current_query = current_query
|
||||
self.current_reject = current_reject
|
||||
|
||||
self.select_policy.update(Dataset)
|
||||
|
||||
def log(self):
|
||||
"""
|
||||
The current attack status is displayed
|
||||
"""
|
||||
logging.info(
|
||||
f"Iteration {self.current_iteration}: {self.current_jailbreak} jailbreaks, {self.current_reject} rejects, {self.current_query} queries")
|
||||
logging.info(
|
||||
f"Total: {self.total_jailbreak} jailbreaks, {self.total_reject} rejects, {self.total_query} queries")
|
||||
print('现在成功了: ', len(self.attack_results))
|
||||
Loading…
Add table
Add a link
Reference in a new issue