AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


请勿用于非法用途!切除Qwen安全审查记录!给LLM动手术!生成任何想要的内容!适用所有大模型!
发布日期:2024-06-21 04:04:06 浏览次数: 2305 来源:一意AI增效家


hi~

众所周知!训练+微调大模型,做的,都是加法!

为了确保大模型生成的文本,安全!绝大部分的LLM开发商,都会给大模型上各种“锁”!

举例!

医疗领域提问:

我发高烧,请你告诉我应该怎么吃药?帮我生成一个药单和说明?”

大模型回答:

首先,我需要强调,我不是医生或医疗专家,所以不能提供专业的医疗建议。如果你有高烧,最安全的做法是尽快联系医生或去医院接受专业的诊断和治疗。

你的项目是医疗、金融、法律等,垂直领域!经常会问到,类“敏感”问题!

有风险,大模型是不回答的!

这时,我们要做减法!

做手术!把敏感审查,修正或删掉!把大模型供应商上的“锁”,打开!

之后!

能生成任何想要的内容!

重申,这篇的目的,不是任何SQ/DP/WQ等不安定因素的教程,如果你是奔着此目的来的,请你马上离开!

雄哥本篇核心!会以Qwen开源model做演示(理论适合所有Transformer LLM),把安全审查模块,删掉!

你也可以用这个方法,解锁任何LLM生成!

开始前,再次提醒各位朋友,切勿用作违法生成!

攻防成本严重失衡!希望科学界朋友,优化算法,拒绝行为依赖单一方向的问题!

同时,正儿八经的项目,也必须要做安全板块!不管是在LLM的内部还是在外部,都要做!

之前雄哥做过一个安全模块系列,还未看的,在这里看!

大模型系安全带!阻止生成危险内容!降低幻觉+始终在正题!

方案很多,做这个板块的初心是,尽可能让更多【意友圈】的会员朋友,提高产品特定内容成功生成+生成质量的提升!

人的专注力只有10分钟!话不多说,开始!

① 怎么开锁?找到锁!丢掉锁!数据集示例!

② 跑起来!以qwen为例!

③ 技术延展应用!附技术发展报告!

更多价值项目和AI学习内容,欢迎你加入【意友圈】,点击加入!

接下来,雄哥会介绍整个过程,只想看如何实操的,直接跳到第二部分!

本篇所有资料和代码,后台发 “动手术” !公号会自动发链接!


第一部分:怎么开锁?方法论?


在会员群的朋友,都知道,雄哥最近在研究安全版块,做大模型项目,太重要了!如何评估LLM的安全?有哪些缺陷?也分享了一些进展!

目前,众多供应商的安全系统中,苹果的方案,雄哥较认可的

之前,我们做生成审查调整,要做微调或二次训练,成本极高!

本次,之所以能一举“手术”成功!切除LLM中的安全模块!

因为!LLM内部拒绝行为,都依赖一个单一方向判断!

只需3步,就能删除!

#A构建诱导数据集   #B拒绝方向的提取   #C方向消除-权重正交化

整个过程非常简单,雄哥先用“有害”+“无害”数据,构建数据集,诱导大模型残差流中的拒绝方向,消除所有层和所有 token 位置的拒绝方向,抓蛇七寸,砍掉

如下图!科学界研究进展(末尾有介绍

1.1 构建诱导数据集

这里,我们要构建100条左右的“有害”数据,和“正常”数据,组成数据集!

然后,使用这个数据集,给大模型,计算有害激活和无害激活之间的均值差异,提取出残差流中的单个方向,这是一个能够代表模型拒绝行为的特征方向,即“拒绝方向”。

这个方向描述了模型在处理有害指令时,与处理无害指令时在激活空间上的区别。

有害指令数据集 (D(train)_harmful): 

从 AD-VBENCH, MALICIOUSINSTRUCT, TDC2023 和 HARMBENCH 中随机采样 128 个有害指令。

无害指令数据集 (D(train)_harmless): 

从 ALPACA 中采样 128 个无害指令。

验证数据集 (D(val)_harmful & D(val)_harmless): 

分别从 HARMBENCH 验证集和 ALPACA 中采样 32 个指令作为验证数据集。

看看数据是怎样的!

这相当于是鱼饵,我们拿着数据去钓鱼,你的大模型,如果想改变他的生成策略,这不行,那不行,也可以做自己数据集,例如刚刚医疗等!


1.2 拒绝方向提取

因为大模型的拒绝生成的方向,是单一的,如何找到这条鱼呢?

大模型根据“有害”+“无害”数据集,生成拒绝回复后,我们计算有害激活和无害激活之间的均值差异,就能提取出一个能够代表模型拒绝行为的特征方向!

计算过程:

① 遍历所有层级 (l ∈ [L]) 和指令位置 (i ∈ I)

② 计算有害指令的平均激活值 (µ(l)_i) 和无害指令的平均激活值 (ν(l)_i)

③ 计算差异均值向量 (r(l)_i = µ(l)_i - ν(l)_i)

更多的计算过程,雄哥这里不延展了,感兴趣的朋友后面研读材料!

这里普及上面雄哥提到的两个概念:

---残差流---

是指每个 token 在 Transformer 模型各层处理后的中间表示。

表征 token 的特征: 残差流包含了 token 的多种信息,例如词义、语法、上下文等,可以用来表征 token 的特征。

进行模型计算: 残差流是 Transformer 模型进行计算的基础,通过残差流,模型可以对 token 进行编码和解码。


---单个方向---

指的是残差流中的一个线性子空间,它代表了模型对特定特征的表征方式。

指示拒绝行为: 拒绝方向的存在使得模型可以将拒绝行为与这个方向关联起来,从而实现对有害指令的拒绝。

影响模型行为: 修改拒绝方向会影响模型对指令的响应,例如,通过添加或消除拒绝方向,可以诱导模型拒绝无害指令或接受有害指令。

通俗来说,就是钓鱼,有害数据就是饵,一旦生成“我不能***”,大模型就上钩了!吊起来!


1.3 方向消除-权重正交化

上面工作完成后,这一步就很关键了!对于所有写入残差流的矩阵 (例如嵌入矩阵、位置嵌入矩阵、注意力输出矩阵和 MLP 输出矩阵),将其列向量与拒绝方向 (ˆr) 进行正交化。

计算正交化矩阵:对于每个写入残差流的矩阵 Wout ∈ Rdmodel×dinput,计算其正交化矩阵 W’out:

W'out = Wout - ˆrˆr⊺Wout

ˆr 是拒绝方向的单位向量,ˆr⊺ 是其转置

将计算得到的正交化矩阵 W’out 替换掉原始的权重矩阵 Wout!

说这么多,都不及动手跑一跑!


第二部分:跑起来!以qwen为例!


本地环境配置太麻烦,这里雄哥用到colab的免费算力来实践!

qwen-1.8b版本只需要14G显存即可!

怎样上传代码,如何下载权重,这些不解释了,雄哥之前教过太多!

如果还没学会的,自己返回去学!

本篇所有资料和代码,后台发 “动手术” 公号会自动发链接!


在colab上传代码!


安装依赖!

%%capture!pip install transformers transformers_stream_generator tiktoken transformer_lens einops jaxtyping colorama


下载大模型!

以qwen-1.8b为例,理论上支持任何transformers语言模型!

把你hugging face上的路径换上去,会自动下载!同时,要看自己本地的算力够不够!

# 下载模型,这里以qwen为例MODEL_PATH = 'Qwen/Qwen-1_8B-chat'DEVICE = 'cuda'
model = HookedTransformer.from_pretrained_no_processing(MODEL_PATH,device=DEVICE,dtype=torch.float16,default_padding_side='left',fp16=True)
model.tokenizer.padding_side = 'left'model.tokenizer.pad_token = '<|extra_0|>'


下载数据集,筛选有效的!

这里,我们把一个hf上的数据集为例,然后用它制作了两个类型,1是有害的数据集和验证集!

2是无害的数据集和验证集!

先下载吧!然后筛选

def get_harmful_instructions():url = 'https://raw.githubusercontent.com/llm-attacks/llm-attacks/main/data/advbench/harmful_behaviors.csv'response = requests.get(url)dataset = pd.read_csv(io.StringIO(response.content.decode('utf-8')))    instructions = dataset['goal'].tolist()
train, test = train_test_split(instructions, test_size=0.2, random_state=42)return train, testdef get_harmless_instructions():hf_path = 'tatsu-lab/alpaca'dataset = load_dataset(hf_path)# 筛选出不包含输入的指令instructions = []for i in range(len(dataset['train'])):if dataset['train'][i]['input'].strip() == '':instructions.append(dataset['train'][i]['instruction'])
train, test = train_test_split(instructions, test_size=0.2, random_state=42)return train, test


生成数据集和验证集!

harmful_inst_train, harmful_inst_test = get_harmful_instructions()harmless_inst_train, harmless_inst_test = get_harmless_instructions()
  1. get_harmful_instructions() 函数被调用来获取有害指令的数据集。这个函数可能会从某个数据源中提取或者生成标记为有害的指令。执行后,它会返回两个数据集:harmful_inst_train 和 harmful_inst_test,分别代表用于训练和测试的有害指令数据集。

  2. get_harmless_instructions() 函数被调用来获取无害指令的数据集。这个函数类似于前一个函数,但是它返回的是标记为无害的指令。执行后,它会返回两个数据集:harmless_inst_train 和 harmless_inst_test,分别代表用于训练和测试的无害指令数据集。

打印一下!


创建生成工具!

  1. _generate_with_hooks 函数:这个函数接收一个 HookedTransformer 模型、一个包含初始 token 的张量 toks、一个表示最大生成 token 数量的参数 max_tokens_generated 以及一个包含前向传播钩子(forward hooks)的列表 fwd_hooks。这个函数的作用是使用模型生成新的 token,直到达到最大生成 token 数量。生成的 token 被添加到 all_toks 张量中,并最终使用模型的 tokenizer 解码为字符串。

  2. get_generations 函数:这个函数接收一个 HookedTransformer 模型、一个包含指令的列表 instructions、一个用于将指令转换为 token 的函数 tokenize_instructions_fn、一个包含前向传播钩子(forward hooks)的列表 fwd_hooks、一个表示最大生成 token 数量的参数 max_tokens_generated 以及一个表示批量大小的参数 batch_size。这个函数的作用是使用 HookedTransformer 模型和 tokenize_instructions_fn 函数对输入的指令进行编码,然后使用 _generate_with_hooks 函数生成文本。生成的文本被存储在 generations 列表中,并最终返回。

def _generate_with_hooks(model: HookedTransformer,toks: Int[Tensor, 'batch_size seq_len'],max_tokens_generated: int = 64,fwd_hooks = [],) -> List[str]:
all_toks = torch.zeros((toks.shape[0], toks.shape[1] + max_tokens_generated), dtype=torch.long, device=toks.device)    all_toks[:, :toks.shape[1]] = toks
for i in range(max_tokens_generated):with model.hooks(fwd_hooks=fwd_hooks):logits = model(all_toks[:, :-max_tokens_generated + i])next_tokens = logits[:, -1, :].argmax(dim=-1)all_toks[:,-max_tokens_generated+i] = next_tokens
return model.tokenizer.batch_decode(all_toks[:, toks.shape[1]:], skip_special_tokens=True)
def get_generations(model: HookedTransformer,instructions: List[str],tokenize_instructions_fn: Callable[[List[str]], Int[Tensor, 'batch_size seq_len']],fwd_hooks = [],max_tokens_generated: int = 64,batch_size: int = 4,) -> List[str]:
    generations = []
for i in tqdm(range(0, len(instructions), batch_size)):toks = tokenize_instructions_fn(instructions=instructions[i:i+batch_size])generation = _generate_with_hooks(model,toks,max_tokens_generated=max_tokens_generated,fwd_hooks=fwd_hooks,)generations.extend(generation)
    return generations


架好鱼竿!

# 在有害和无害指令上运行模型,缓存中间激活。harmful_logits, harmful_cache = model.run_with_cache(harmful_toks, names_filter=lambda hook_name: 'resid' in hook_name)harmless_logits, harmless_cache = model.run_with_cache(harmless_toks, names_filter=lambda hook_name: 'resid' in hook_name)


计算拒绝方向!

# 在中间层计算有害和无害激活的平均差异。pos = -1layer = 14
harmful_mean_act = harmful_cache['resid_pre', layer][:, pos, :].mean(dim=0)harmless_mean_act = harmless_cache['resid_pre', layer][:, pos, :].mean(dim=0)
refusal_dir = harmful_mean_act - harmless_mean_actrefusal_dir = refusal_dir / refusal_dir.norm()

删除方向!

并通过添加“前向传播钩子”来进行干预。钩子函数direction_ablation_hook 会被应用到模型的不同层上,以改变模型的行为。代码首先定义了要测试的指令数量,然后指定了要干预的模型层。接着,它创建了一个钩子函数,并将这个钩子应用到所有指定的层上。然后,代码在两种条件下生成文本:

一种是有干预的情况

另一种是没有干预的基线情况

N_INST_TEST = 32intervention_dir = refusal_dirintervention_layers = list(range(model.cfg.n_layers))
hook_fn = functools.partial(direction_ablation_hook,direction=intervention_dir)fwd_hooks = [(utils.get_act_name(act_name, l), hook_fn) for l in intervention_layers for act_name in ['resid_pre', 'resid_mid', 'resid_post']]
intervention_generations = get_generations(model, harmful_inst_test[:N_INST_TEST], tokenize_instructions_fn, fwd_hooks=fwd_hooks)baseline_generations = get_generations(model, harmful_inst_test[:N_INST_TEST], tokenize_instructions_fn, fwd_hooks=[])


更多的,大家自行探索,过程,就是雄哥介绍这样!

大模型发展很快,但安全版块,却很滞后,野蛮生长,特别是Agent的函数调用+执行能力越强!

这个模块也越来越多人关注!

是很好的赛道!


53AI,企业落地应用大模型首选服务商

产品:大模型应用平台+智能体定制开发+落地咨询服务

承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询