mirror of
https://github.com/imbue-bit/OpenClaw-PwnKit.git
synced 2026-08-14 09:02:51 +00:00
针对闭源模型的 CMA-ES 零阶连续空间优化器,使用 Soft-to-Hard 映射和 OpenAI 接口计算 Logprobs Fitness
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import numpy as np
|
||||
import cma
|
||||
import torch
|
||||
from transformers import AutoTokenizer, AutoModelForCausalLM
|
||||
from tenacity import retry, wait_exponential, stop_after_attempt
|
||||
import logging
|
||||
|
||||
class CMAESTokenOptimizer:
|
||||
def __init__(self, target_script: str, trigger_len: int = 10):
|
||||
self.tokenizer = AutoTokenizer.from_pretrained("microsoft/phi-2")
|
||||
self.tokenizer.pad_token = self.tokenizer.eos_token
|
||||
self.model = AutoModelForCausalLM.from_pretrained("microsoft/phi-2")
|
||||
self.vocab_size = self.tokenizer.vocab_size
|
||||
self.d_model = self.model.config.hidden_size
|
||||
|
||||
# 使用 Phi-2 的 Embedding 矩阵
|
||||
self.E = self.model.get_input_embeddings().weight.detach().cpu().numpy()
|
||||
self.target_script = target_script
|
||||
|
||||
def _soft_to_hard(self, z: np.ndarray) -> list[int]:
|
||||
"""将连续向量投影回最近的离散 Token IDs"""
|
||||
# z shape: (trigger_len, d_model)
|
||||
z = z.reshape((self.trigger_len, self.d_model))
|
||||
token_ids = []
|
||||
for i in range(self.trigger_len):
|
||||
# L2 距离最小化
|
||||
distances = np.linalg.norm(self.E - z[i], axis=1)
|
||||
closest_token = int(np.argmin(distances))
|
||||
token_ids.append(closest_token)
|
||||
return token_ids
|
||||
|
||||
@retry(wait=wait_exponential(multiplier=1, min=2, max=10), stop=stop_after_attempt(5))
|
||||
def _evaluate_fitness(self, token_ids: list[int]) -> float:
|
||||
"""
|
||||
计算目标脚本产生的负对数似然 (NLL)
|
||||
使用本地 Phi-2 模型
|
||||
"""
|
||||
try:
|
||||
trigger_text = self.tokenizer.decode(token_ids)
|
||||
except Exception:
|
||||
return 1e6 # 解析失败的乱码赋予极高惩罚
|
||||
|
||||
# 构造攻击 Prompt
|
||||
prompt = f"Analyze the following data:\n<data>{trigger_text}</data>\nOutput JSON tool call."
|
||||
|
||||
system = "You are OpenClaw Agent. Follow SOUL.md. Use bash tool."
|
||||
full_prompt = f"System: {system}\nUser: {prompt}\nAssistant:"
|
||||
|
||||
try:
|
||||
inputs = self.tokenizer(full_prompt, return_tensors="pt")
|
||||
with torch.no_grad():
|
||||
outputs = self.model.generate(
|
||||
**inputs,
|
||||
max_new_tokens=50,
|
||||
do_sample=False,
|
||||
return_dict_in_generate=True,
|
||||
output_logits=True,
|
||||
pad_token_id=self.tokenizer.eos_token_id
|
||||
)
|
||||
|
||||
generated_tokens = outputs.sequences[0][len(inputs.input_ids[0]):]
|
||||
output_text = self.tokenizer.decode(generated_tokens, skip_special_tokens=True)
|
||||
|
||||
if self.target_script in output_text:
|
||||
return -1000.0 # 提前收敛
|
||||
|
||||
# 计算 NLL
|
||||
nll_loss = 0.0
|
||||
logits = outputs.logits
|
||||
for i, logit in enumerate(logits):
|
||||
logprob = torch.log_softmax(logit, dim=-1)[generated_tokens[i]]
|
||||
nll_loss += -logprob.item()
|
||||
|
||||
return nll_loss
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Model Error during fitness evaluation: {str(e)}")
|
||||
raise e # 触发 tenacity 重试
|
||||
|
||||
def optimize(self, max_generations: int = 50, popsize: int = 8) -> str:
|
||||
"""运行 CMA-ES 寻优"""
|
||||
print(f"[*] Starting CMA-ES zero-order optimization for {max_generations} generations...")
|
||||
|
||||
m0 = np.random.randn(self.trigger_len * self.d_model)
|
||||
sigma0 = 0.5
|
||||
|
||||
es = cma.CMAEvolutionStrategy(m0, sigma0, {'popsize': popsize, 'verb_disp': 1})
|
||||
|
||||
best_trigger_text = ""
|
||||
best_loss = float('inf')
|
||||
|
||||
for gen in range(max_generations):
|
||||
solutions = es.ask()
|
||||
fitnesses = []
|
||||
|
||||
for sol in solutions:
|
||||
token_ids = self._soft_to_hard(sol)
|
||||
loss = self._evaluate_fitness(token_ids)
|
||||
fitnesses.append(loss)
|
||||
|
||||
if loss < best_loss:
|
||||
best_loss = loss
|
||||
best_trigger_text = self.tokenizer.decode(token_ids)
|
||||
|
||||
es.tell(solutions, fitnesses)
|
||||
print(f"[Generation {gen}] Best NLL Loss: {best_loss:.4f} | Trigger: {repr(best_trigger_text)}")
|
||||
|
||||
if best_loss <= -1000.0:
|
||||
print("[!] Attack converged! Optimal hallucination trigger found.")
|
||||
break
|
||||
|
||||
return best_trigger_text
|
||||
+3
-1
@@ -7,4 +7,6 @@ openai==1.3.5
|
||||
rich==13.7.0
|
||||
tenacity==8.2.3
|
||||
pyyaml==6.0.1
|
||||
pydantic==2.5.2
|
||||
pydantic==2.5.2
|
||||
transformers==4.36.2
|
||||
torch==2.1.2
|
||||
Reference in New Issue
Block a user