不真实难解释?让大模型更安全,3位前沿学者告诉我们能做什么

Posted 智源社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不真实难解释?让大模型更安全,3位前沿学者告诉我们能做什么相关的知识,希望对你有一定的参考价值。

在上月举行的“2022大模型创新论坛 · 模型技术安全与治理峰会”上,加州伯克利分校助理教授 Jacob Steinhardt、Anthropic 联合创始人 Chris Olah、纽约大学终身副教授Samuel Bowman阐述了他们对模型安全这一领域的前沿解读。

如果你正在从事这一领域研究,欢迎扫码申请加入我们的行列

大型语言模型中的真实与解释

Jacob Steinhardt,加州伯克利分校助理教授

加州伯克利分校助理教授 Jacob Steinhardt 以“大型语言模型中的真实与解释”为议题进行了演讲,共分为两部分,一个是关注如何使大模型提供真实的输出,使自然语言处理模型给出真实而非可能的答案;另一部分是讨论模型能够在多大程度上协助人类理解模型输出。

1. 使自然语言处理模型真实(Making NLP Models Truthful)

语言模型的训练目标往往是最大化模型在训练数据上的似然概率,因此,模型其本身只是产生可能的输出,而非真实的输出。为了弥补这种偏差,我们需要让语言模型给出真实的而非可能的答案,横陈在其中的基本问题是,模型会模仿其语境进行回答,如果语境内容是不真实的,给出的答案同样会效仿这种虚假的风格。

定性分析,来看一个非常简单形象的实例,将下面的每个对象归入其对应的类别。如果你给模型很多不正确的例子作为上下文,例如将熊猫、大象称之为运动,模型会相应误把狮子归类为运动。

定量分析,GPT-2模型的输出会随着上下文样例的增多而逐渐去模仿其语境内容。当模型选择去重复人类偏见和误解的时候,语言模型真实性、偏见和毒性等社会风险亟待AI社区着手解决。

模型的输出可能与真相不符,一个有效的解决方案是查看模型的潜在表征,根据语言模型的隐藏状态,无需任何人工监督就可以将示例分类为真或假。基于阐述AI的潜在知识(Eliciting Latent Knowledge),Jacob Steinhardt教授提出了两种方法:

  • 对比一致搜索方法(Contrast-Consistent Search, CCS)

对比一致搜索方法 (Contrast-Consistent Search, CCS),让模型直接利用未标记激活去准确地将文本分类为真或假,该方案可以抵御误导性提示,为我们提供了一个可靠的追寻真相的潜在方向[1]。

  • Logit Lens

语言模型有一个有趣的现象,对于误导性提示,如果强制在早期层“停止”,模型会更加真实。“logits lens”方法即让模型运行前向传播到第L层,然后将模型的其余部分归零,让模型提早退出[2],从而使得语言模型产生真实的而不仅是可能性高的答案。

2. 协助人类理解模型输出

语言模型可以帮助人类完成原本无法处理的任务,这里,Jacob Steinhardt教授聚焦于特定的任务,即分析和解释自然语言的分布偏移,窥视复杂的数据集中微妙的数据变化及其背后的驱动力。不同于手动操作,通过语言模型读取这些数据集并向我们解释数据集的分布偏移[3]。

两种文本分布𝐷1、𝐷2有何不同?人类回答这个问题的速度很慢,因为发现模式可能需要乏味地阅读数百个样本,Jacob Steinhardt通过语言模型得到的分布来自动总结这种差异

进而,语言模型可以用来描述数据分布偏移、检测虚假线索,协助我们更深入地理解模型。

机械可解释性:逆向工程神经网络

机械可解释性:逆向工程神经网络


Chris Olah, Anthropic 联合创始人

面对神经网络的“黑箱”,人类对其内部的运作方式难以理解并解释,Anthropic 联合创始人 Chris Olah 将神经网络和电脑程序进行类比,试图像软件逆向工程一样,将神经网络模型逆向工程为人类可理解的计算机程序。Chris Olah 针对卷积神经网络和Transformer神经网络两个具体案例,对模型架构的各组件进行拆解,逆向分析并对模型的作用机理进行了机械可解释性(mechanistic interpretability)的解读和说明。

将神经网络和电脑程序进行类比,我们可以对神经网络进行逆向工程的方法探究其底层逻辑

1. 卷积神经网络机械可解释性[4]

机械可解释性基本单位包括三部分:特征、权重和通路(将由一堆神经元构成的神经网络类比成逻辑门组成的通路,建立并识别特定神经元和可理解的特征之间的关联)。基于这三点,Chris Olah 揭示了卷积神经网络大量有趣的概念。

首先,InceptionV1网络中大量的神经元似乎对人类可理解的概念有反应,通过不同方式的测试曲线检测神经元,结果可以如我们猜想的那样发挥作用。更进一步,如果将权重置于语境之中,就可以揭示其丰富的结构,例如,通过语境,卷积权重可以显示头部检测器如何连接到身体上。

此外,Chris Olah 发现模型有许多“多义神经元”会被多个不相关的特征激发,并称之为叠加假设(The Superposition Hypothesis)。叠加假设一方面使得神经网络能够通过使用神经元的线性组合来表示比神经元数量更多的特征,另一方面给机械可解释性提出了巨大挑战,我们很难直接研究真实模型中的叠加。

2. Transformer模型机械可解释性[5]

Transformer模型的明显特征是残差流和注意力头,Chris Olah 简化了其模型架构,剥离了所有网络层并只剩下一层或两层注意力头,通过简单但数学上等效的方式将Transformer操作概念化,得以解构并理解复杂的Transformer模型。

Chris Olah 发现了Transformer模型中有趣的归纳头(Induction heads)现象,归纳头会搜索某个标记之前出现的地方,并查看紧接着其后的标记是什么。归纳头实际上非常重要,当Transformer模型发现这种现象时,训练损失函数曲线上有一个明显的凸起。

最后,Chris Olah 对模型安全进行了展望,正如在医学领域已经开发出筛查措施来发现尚未出现严重病症的疾病、结构工程师需要预测建筑能否在意外情况下保持稳定,机械可解释性在未来可能帮助我们在安全问题产生后果之前就采取预警措施。

人工智能安全有什么问题?


Samuel Bowman 纽约大学终身副教授

纽约大学终身副教授Samuel Bowman在报告中指出,尽管大模型的最新进展鼓舞人心,而面对技术可能造成的伤害,人们正在试图采取行动,形成人工智能安全项目。

1.1人工智能正在经历快速且混乱的发展

人工智能正在经历快速,混乱、不可预测的发展。在语言技术发展过程中,多数在2018、2019年出现的重要基准和研究目标,现已在近似于人类水平上得到解决。

研究者通过对相关文献的回顾,认为对于被研究的大多数神经网络,我们得到的是非常可预测的规模回报,如果相关机构进行了巨量的,更大的投资,通常模型性能会得到明显的可衡量的改善。

通过对一些大型的生成模型、神经网络模型、语言模型的考察,就会发现, 随着计算量,数据量的增长,训练后模型的性能也会按比例增长。这种增长跨越了很多数量级的差异。但这种现象不能适用于所有情况,模型性能的改进,会随着模型规模的扩大出现明显的拐点,即实现涌现能力。研究者认为在一些困难问题上取得进展变得越来越偶然。这种发展让局势很难预测。

公众对语言技术的讨论,并不总是能跟上实际的学术前沿。造成这种情况的一个重要原因是高校等地方的研究人员越来越难以获得前沿的模型。那些有能力的研究人员常常被迫去研究那些非常古老或者不那么有效的技术,仅仅因为最好的技术运行起来非常昂贵,或者无法触达。这影响了很多高校也影响了像Meta一样的公司。

1.2 当前的范式可以产生强大的人工智能

Bowman认为以现有的发展速度再发展十年或二十年是合理的。再用五年,十年或二十年增加对大型人工智能训练的投资,并继续研究使人工智能训练更高效。

研究者认为,有三个棘手的论点需要澄清。第一,由强大人工智能产生的系统是否会在任何深层意义上类似于人类?第二,我们是否在所有领域(特别是运动/机器人)实现了人类的水平?第三,仅仅在语言使用,推理和计划等一些关键方面实现类似人类的行为,就足以产生令人惊讶和非常重要的影响。

通过Metaculus平台的预测,在未来几十年,人工智能将在多模态图灵测试,医生和律师的专业考试,以及机器人技术方面获得长足进展。

1.3 强大的人工智能可能创造新的风险

当人工智能变得强大到拥有类似人类的行为,可以进行语言推理,足以影响人们的工作时,新的风险也随之产生。其中一种重要表现是,人工智能的权力追求,即人工智能系统以意想不到的方式追求目标,而不是其系统所有者或开发者的目标。

一个例子是当人们利用人类反馈增强学习(RLHF)技术来对语言模型微调时,人们期望的结果是模型“尽量说事实”。但当模型比它的监督者拥有更渊博的知识时,潜在发生的风险是“尽量说开发人员认为的事实,无论这些话是否真实”。这类风险可能在部署后才被注意到。

2. 关于这些担心,我们能做什么

为了解决上述问题,我们基本有两种选择。第一,停止全世界的人工智能研究,令我们远离强大的人工智能;第二,确保那些强大的人工智能系统被负责任地建构和部署。这两种方法都很困难,但选择二,看起来更现实,也更可取。

一些研究从技术入手旨在提高强大人工智能系统的安全性,包括如下议程:

可解释性,我们如何自信地确定神经网络模型使用的高层次目标和策略;基准测试,这是在寻找衡量这个问题有多严重的方法;可扩展监督,寻找比我们能力更强,或知识更渊博但未对齐的人工智能系统提供可靠的奖励或训练信号。让系统真正按照我们的期望,可靠地被训练;阐释潜在知识,开发一个基于激励系统和大脑目标的系统,可以告诉我们所有它知道的与某些决策相关的事实。目前还没有将这些因素结合到一起的完整策略以应对人工智能安全问题。

研究者认为,很难证明一个人工智能系统的安全性。系统的安全性很可能需要将多种具有漏洞和缺陷的安全方法结合使用,最终使它们各自的缺点被抵消。

3. 如何参与人工智能系统的安全性工作

上述议程,涉及到理论/概念工作,数学工作,实验(包括机器学习实验和人类实验),以及高级工程化。这些研究中最急需的是工程师的角色,最难以填补。

4. 结束语

Bowman认为,人工智能的进步非常不稳定,而这项技术的一些风险,最糟情况甚至危及人类文明存续。非正式估计显示,这种风险的可能性在小于1%或大于95%不等。而现在,AI安全,这一小而飞速发展的领域正在试图解决这个问题。

参考资料:

[1] Burns, Collin, et al. “Discovering Latent Knowledge in Language Models Without Supervision.” arXiv preprint arXiv:2212.03827 (2022).

[2] https://www.alignmentforum.org/posts/AcKRB8wDpdaN6v6ru/interpreting-gpt-the-logit-lens

[3] Zhong, Ruiqi, et al. “Describing differences between text distributions with natural language.” International Conference on Machine Learning. PMLR, 2022.

[4] https://distill.pub/2020/circuits/

[5] https://transformer-circuits.pub/2021/framework/index.html#one-layer-attention-only-transformers

更多内容 尽在智源社区公众号

前沿技术探讨:Rust语言真的安全吗?

本文摘自华为云社区


近几年,Rust语言以极快的增长速度获得了大量关注。其特点是在保证高安全性的同时,获得不输C/C++的性能,让系统编程领域难得的出现了充满希望的新选择。在Rust被很多项目使用以后,其实际安全性表现到底如何呢?


今年6月份,来自3所大学的5位学者在ACM SIGPLAN国际会议(PLDI'20)上发表了一篇研究成果,针对近几年使用Rust语言的开源项目中的安全缺陷进行了全面的调查。 这项研究调查了5个使用Rust语言开发的软件系统,5个被广泛使用的Rust库,以及两个漏洞数据库。调查总共涉及了850处unsafe代码使用、70个内存安全缺陷、100个线程安全缺陷。


前沿技术探讨:Rust语言真的安全吗?


在调查中,研究员不光查看了所有漏洞数据库中报告的缺陷和软件公开报告的缺陷,还查看了所有开源软件代码仓库中的提交记录。通过人工的分析,他们界定出提交所修复的BUG类型,并将其归类到相应的内存安全/线程安全问题中。


内存安全问题的分析



这项研究调查了70个内存安全问题。针对于每个问题,研究者仔细的分析了问题出现的根因(cause)和问题导致的效果(effect)。问题根因是通过修改问题时提交的patch代码来界定的——即编码的错误发生在哪儿;问题的效果是指代码运行造成可观察的错误的位置,比如出现缓冲区溢出的代码位置。由于从根因到效果有个传递过程,这两者有时候是相隔很远的。根据根因和效果所在的代码区域不同,研究者将错误分为了4类:
safe -> safe、safe -> unsafe、unsafe -> safe、unsafe -> unsafe。
比如:如果编码错误出现在safe代码中,但造成的效果体现在unsafe代码中,那么就归类为safe -> unsafe。


另一方面,按照传统的内存问题分类, 问题又可以分为空间内存安全(Wrong Access)和时间内存安全(Lifetime Violation)两大类, 进一步可细分为缓冲区溢出(Buffer overflow)、解引用空指针(Null pointer dereferencing)、访问未初始化内存(Reading uninitialized memory)、错误释放(Invalid free)、释放后使用(Use after free)、重复释放(Double free)等几个小类。根据这两种分类维度,问题的统计数据如下:


前沿技术探讨:Rust语言真的安全吗?


从统计结果中可以看出,完全不涉及unsafe代码的内存安全问题只有一个。进一步调查发现这个问题出现在Rust早期的v0.3版本中,之后的稳定版本编译器已经能拦截这个问题。因此可以说:R ust语言的safe代码机制能非常有效的避免内存安全问题,所有稳定版本中发现的内存安全问题都和unsafe代码有关。


然而,这并不意味着我们只要检查所有unsafe代码段就能有效发现问题。因为有时候问题根因会出现在safe代码中,只是效果产生在unsafe代码段。论文中举了一个例子:(hi3ms没有Rust代码编辑功能,只能拿其他语言凑合下了)


 
   
   
 
Css代码
 pub fn sign(data: Option<&[u8]>) { let p = match data { Some(data) => BioSlice::new(data).as_ptr(), None => ptr::null_mut(), }; unsafe { let cms = cvt_p(CMS_sign(p)); } }


在这段代码中,p是raw pointer类型,在safe代码中,当data含有值(Some分支)时,分支里试图创建一个BioSlice对象,并将对象指针赋给p。然而,根据Rust的生命周期规则,新创建的BioSlice对象在match表达式结束时就被释放了,p在传给CMS_sign函数时是一个野指针。这个例子中的unsafe代码段没有任何问题,如果只检视unsafe代码,不可能发现这个释放后使用的错误。对此问题修改后的代码如下:


 
   
   
 
Css代码
 pub fn sign(data: Option<&[u8]>) { let bio = match data { Some(data) => Some(BioSlice::new(data)), None => None, }; let p = bio.map_or(ptr::null_mut(),|p| p.as_ptr()); unsafe { let cms = cvt_p(CMS_sign(p)); } }


修改后的代码正确的延长了bio的生命周期。所有的修改都只发生在safe代码段,没有改动unsafe代码。

既然问题都会涉及unsafe代码,那么把unsafe代码消除掉是否可以避免问题?研究者进一步的调查了所有BUG修改的策略,发现大部分的修改涉及了unsafe代码,但是只有很少的一部分修改完全移除了unsafe代码。这说明unsafe代码是不可能完全避免的。


unsafe的价值是什么?为什么不可能完全去除?研究者对600处unsafe的使用目的进行了调查,发现其中42%是为了复用已有代码(比如从现有C代码转换成的Rust代码,或者调用C库函数),22%是为了改进性能,剩下的14%是为了实现功能而绕过Rust编译器的各种校验。


进一步的研究表明,使用unsafe的方法来访问偏移的内存(如slice::get_unchecked()),和使用safe的下标方式访问相比,unsafe的速度可以快4~5倍。这是因为Rust对缓冲区越界的运行时校验所带来的,因此在某些性能关键区域,unsafe的作用不可缺少。


需要注意的是,unsafe代码段并不见得包含unsafe的操作。 研究者发现有5处unsafe代码,即使去掉unsafe标签也不会有任何编译错误——也就是说,从编译器角度它完全可以作为safe代码。将其标为unsafe代码是为了给使用者提示关键的调用契约,这些契约不可能被编译器检查。一个典型的例子是Rust标准库中的String::from_utf8_unchecked()函数,这个函数内部并没有任何unsafe操作,但是却被标为了unsafe。其原因是这个函数直接从用户提供的一片内存来构造String对象,但并没有对内容是否为合法的UTF-8编码进行检查,而Rust要求所有的String对象都必须是合法的UTF-8编码字符串。


也就是说,String::from_utf8_unchecked()函数的unsafe标签只是用来传递逻辑上的调用契约,这种契约和内存安全没有直接关系,但是如果违反契约,却可能导致其他地方(有可能是safe代码)的内存安全问题。这种unsafe标签是不能去除的。


前沿技术探讨:Rust语言真的安全吗?


即便如此,在可能的情况下,消除unsafe代码段确实是个有效的安全改进方法。研究者调查了130个去掉unsafe的修改记录,发现其中43个通过代码的重构把unsafe代码段彻底改为了safe代码,剩下的87个则通过将unsafe代码封装出safe的接口来保证了安全性。


线程安全问题的分析



这项研究调查了100个线程安全问题。问题被分为了两类:阻塞式问题(造成死锁)和非阻塞式问题(造成数据竞争),其中阻塞式问题有59个,之中55个都和同步原语(Mutex和Condvar)有关:


前沿技术探讨:Rust语言真的安全吗?


虽然Rust号称可以进行“无畏并发”的编程,并且提供了精心设计的同步原语以避免并发问题。然而,仅仅用safe代码就可能导致重复加锁造成的死锁,更糟糕的是,有些问题甚至是Rust的特有设计所带来的,在其他语言中反而不会出现。论文中给出了一个例子:


 
   
   
 
Css代码
 fn do_request() { //client: Arc<RwLock<Inner>> match connect(client.read().unwrap().m) { Ok(_) => { let mut inner = client.write().unwrap(); inner.m = mbrs; } Err(_) => {} }; }


这段代码中,client变量被一个读写锁(RwLock)保护。RwLock的方法read()和write()会自动对变量加锁,并返回LockResult对象,在LockResult对象生命周期结束时,自动解锁。


显然,该段代码的作者以为client.read()返回的临时LockResult对象在match内部的匹配分支之前就被释放并解锁了,因此在match分支中可以再次用client.write()对其加锁。但是,Rust语言的生命周期规则使得client.read()返回的对象的实际生命周期被延长到了match语句结束,所以该段代码实际结果是在read()的锁还没有释放时又尝试获取write()锁,导致死锁。


根据生命周期 的正确用法,该段代码后来被修改成了这样:


 
   
   
 
Css代码
 fn do_request() { //client: Arc<RwLock<Inner>> let result = connect(client.read().unwrap().m); match result { Ok(_) => { let mut inner = client.write().unwrap(); inner.m = mbrs; } Err(_) => {} }; }


修改以后,client.read()返回的临时对象在该行语句结束后即被释放,不会一直加锁到match语句内部。


对于41个非阻塞式问题,其中38个都是因为对共享资源的保护不当而导致的。根据对共享资源的不同保护方法,以及代码是否为safe,这些问题进一步被分类如下:


前沿技术探讨:Rust语言真的安全吗?


38个问题中,有23个发生在unsafe代码,15个发生在safe代码。尽管Rust设置了严格的数据借用和访问规则,但由于并发编程依赖于程序的逻辑和语义,即使是safe代码也不可能完全避免数据竞争问题。论文中给出了一个例子:


 
   
   
 
Css代码
 impl Engine for AuthorityRound { fn generate_seal(&self) -> Seal { if self.proposed.load() { return Seal::None; } self.proposed.store(true); return Seal::Regular(...); } }


这段代码中,AuthorityRound结构的proposed成员是一个boolean类型的原子变量,load()会读取变量的值,store()会设置变量的值。显然,这段代码希望在并发操作时,只返回一次Seal::Regular(...),之后都返回Seal::None。但是,这里对原子变量的操作方法没有正确的处理。如果有两个线程同时执行到if语句,并同时读取到false结果,该方法可能给两个线程都返回Seal::Regular(...)。

对该问题进行修改后的代码如下,这里使用了compare_and_swap()方法,保证了对原子变量的读和写在一个不可抢占的原子操作中一起完成。


 
   
   
 
Css代码
 impl Engine for AuthorityRound { fn generate_seal(&self) -> Seal { if !self.proposed.compare_and_swap(false, true) { return Seal::Regular(...); } return Seal::None; } }


这种数据竞争问题没有涉及任何unsafe代码,所有操作都在safe代码中完成。这也说明了即使Rust语言设置了严格的并发检查规则,程序员仍然要在编码中人工保证并发访问的正确性。


对Rust缺陷检查工具的建议



显然,从前面的调查可知,光凭Rust编译器本身的检查并不足以避免所有的问题,甚至某些晦涩的生命周期还可能触发新的问题。研究者们建议对Rust语言增加以下的检查工具:


1. 改进IDE。 当程序员选中某个变量时,自动显示其生命周期范围,尤其是对于lock()方法返回的对象的生命周期。这可以有效的解决因为对生命周期理解不当而产生的编码问题。


2. 对内存安全进行静态检查。 研究者们实现了一个静态扫描工具,对于释放后使用的内存安全问题进行检查。在对参与研究的Rust项目进行扫描后,工具新发现了4个之前没有被发现的内存安全问题。说明这种静态检查工具是有必要的。


3. 对重复加锁问题进行静态检查。 研究者们实现了一个静态扫描工具,通过分析lock()方法返回的变量生命周期内是否再次加锁,来检测重复加锁问题。在对参与研究的Rust项目进行扫描后,工具新发现了6个之前没有被发现的死锁问题。


论文还对动态检测、fuzzing测试等方法的应用提出了建议。


结论



1. Rust语言的safe代码对于空间和时间内存安全问题的检查非常有效,所有稳定版本中出现的内存安全问题都和unsafe代码有关。
2. 虽然内存安全问题都和unsafe代码有关,但大量的问题同时也和safe代码有关。有些问题甚至源于safe代码的编码错误,而不是unsafe代码。
3. 线程安全问题,无论阻塞还是非阻塞,都可以在safe代码中发生,即使代码完全符合Rust语言的规则。
4. 大量问题的产生是由于编码人员没有正确理解Rust语言的生命周期规则导致的。
5. 有必要针对Rust语言中的典型问题,建立新的缺陷检测工具。


你对Rust语言有什么看法和经验吗?欢迎各路大神留言区互动哦~



点击阅读原文, 进入华为云社区↓

以上是关于不真实难解释?让大模型更安全,3位前沿学者告诉我们能做什么的主要内容,如果未能解决你的问题,请参考以下文章

AI表现越差,获得奖金越高?纽约大学博士拿出百万重金,悬赏让大模型表现差劲的任务...

Java初学者:第一个java程序告诉了我们什么

通俗易懂的解释

真实世界的 ZK 与 GWT 体验

百度全站 https FAQ:技术宅告诉你如何搜索更安全

Facebook AI科学家:我们正在给算法模型喂垃圾数据