代码能自我意识吗?来自麻省理工学院学习计算机科学的思考

我记得第一次想到自我意识的时候,就像很多事情一样,因为看了太多科幻小说。《星际迷航:下一代》(Star Trek: the Next Generation)中的机器人“数据”(Data)是有意识的存在,还是只是一台“机器”?

代码能自我意识吗?来自麻省理工学院学习计算机科学的思考_第1张图片

自我意识是数据?皮卡德认为是的。

好的机器人是一回事,它们具有物理存在,并且无论它们是否意识清醒,大多数机器都将具有一些基本的自我诊断能力,并意识到其“身体”的状态。 现在,即使是汽车,无论车门是否打开或安全带是否打开,都可以“察觉”。

有一些机器人通过了Mirror Test( 在此处了解有关信息 )的示例,该测试是由Gallop在1970年创建的,目的是观察一种动物(或多种动物)是否可以识别自己。 根据盖洛普(Gallop)的说法,大多数婴儿直到至少18个月大时才通过这项测试,这是存在一定水平的“自我意识”的点(至少通过这项测试衡量)。

那代码呢?

但是这里的问题不是机器人能意识到自身,而是计算机程序能意识到自身吗? 在ST:TNG中,是企业上的“计算机”,仅仅是软件,具有自我意识? 这完全是另一个问题。

直到我在麻省理工学院学习计算机科学并学习了有关编程和递归的所有知识后,我才真正开始思考代码对自身的了解意味着什么的问题。

像往常一样,正是科幻小说给了我一个思考的机会。

1991年,在我去麻省理工学院学习计算机科学的时间快要结束时,电影《终结者2》问世了,它的台词永远不会离开我。

代码能自我意识吗?来自麻省理工学院学习计算机科学的思考_第2张图片

天网变得自我意识不是一件好事……但这意味着什么?

这个化身中的终结者(由阿诺德·施瓦辛格扮演)很友善(将来会由约翰·康纳重新编程)。 莎拉·康纳(Linda Hamilton)正在询问他有关天网的发展(未来试图消灭人类的AI):

终结者:天网资助法案已通过。 该系统于1997年8月4日上线。从战略防御中删除了人为决策。 天网开始以几何速度学习。

如看过这些电影的任何人都知道下一步会发生什么,正如Arnold在现在著名的一行中解释的那样:

终结者:在东部时间8月29日凌晨2点14分,它会自我感知。

当然,埃隆·马斯克和其他人(从牛津哲学家尼克·博斯特罗姆,麻省理工学院物理学家马克斯·泰格马克(Max Tegmark)到斯蒂芬·霍金(Stephen Hawking)的不可避免反应是警告我们未来可能发生。

终结者:惊慌失措,他们试图拔掉插头。

莎拉·康纳(Sarah Conner)独自弄清楚接下来会发生什么:

莎拉·康纳(Sarah Conner):天网反击……

除了世界末日的情景,我还记得我想知道对于天网来说,这是一种AI(据我所知,是一组代码)是自我意识的吗?

代码是否有可能“意识到”自身?

自我意识和杀手级计划……也许?

那年早些时候(或者可能是前一年,我不记得了),我在一个计算机工程课程6.004(计算结构)中偶然地偶然遇到了一个类似的问题,这是EECS系的课程,我们学会了从头开始构建一台计算机,称为Maybe机器。

我们在校园内携带了一个棕色的小手提箱,里面装有一系列的电线和数字电路,它们基本上与我们正在学习的汇编代码一对一地联系在一起(我在家中用Apple II计算机做了一些汇编级编程,不知道我正在编码的小指令直接向处理器发出指令以在物理寄存器中执行X或Y!)。

在课堂上,我们建立了一个原始的操作系统,并且当教练决定举行比赛时,我们正在学习运行的多个流程。 竞争是两个程序将在同一系统上作为单独的进程运行,如果您愿意,则是程序之争。 我们的目标是让我们的计算机程序与其他计算机程序抗衡或禁用。

当今大多数计算机的基本设计都来自冯·诺依曼(Von Neumann),他将内存和存储视为计算机的关键部分。 正在运行的程序会将其指令以及该程序正在处理的数据存储在内存中。 在运行多个程序的系统中(在Macbook上考虑多个窗口),系统会将一个程序换为另一个。 这种基本结构(以相同的方式存储代码和数据)会产生有趣的结果,我们将在本文后面看到。

那年的一个非常寒冷且下雪的夜晚,我在同班同学Ranjan的陪同下,从校园内的实验室里拿出了可能的机器/手提箱,Ranjan在唱歌,因为他整个学期几乎都在校园里走动(“海湾蜜蜂...不要说出马来语”)。

我对Ranjan说了晚安,然后回到我的宿舍制定了一个计划,计划如何编写可以竞争的程序。 我记得比赛是一项可选的作业,但我认为由于我在课堂上表现不错,所以我会尝试一下。

很明显,要使一个程序杀死另一个程序,就需要知道另一个程序以及它在内存中的运行位置。 我写了一个小程序,弄清楚哪个程序正在运行,然后它将找到“另一个”程序在内存中的位置,然后将其覆盖。

最初,这个小程序引发了一些实际问题,这在我脑海中引发了一些哲学问题。 它并没有修改磁盘上的“其他”程序(实际上,我们没有磁盘,而是某种EPROM,据我所记得-那是25年前),但是在内存中对其进行了修改。 但是我如何确定相反的程序将停止运行?

我的第一个解决方案是在应该执行的下一条指令上写一个随机数。 然后我意识到一个随机数不能保证程序会停止运行,只会使程序做随机的事情(让我们开始了解“随机”在计算机中并不是真正的“随机”这一事实)。

取而代之的是,我只是简单地写了一个“零”作为下一个“其他”程序应该执行的指令,这是“停止”运行该程序的信号。

代码能自我意识吗?来自麻省理工学院学习计算机科学的思考_第3张图片

在麻省理工学院的雪地里携带着机器

就像当时那样,大约凌晨3点,哲学开始蔓延,我开始怀疑我所编写的代码是否“意识到”自身? 从某种意义上说,这是必须的,并且这种意识已编码到程序本身中……否则,它无法判断它正在覆盖哪个程序! 它可能会覆盖自己!

大约凌晨4点,我意识到程序代码有可能自行重写! 通常,用C或BASIC或Java这样的语言编写的程序不会自行修改。 我年轻的编程人员甚至没有想到可以做到这一点,但是对于汇编代码,这就像更改内存中的任何其他值一样。

稍后,我会意识到这是计算机科学领域中一个比较好的研究领域,称为自编辑程序。 我意识到唯一可能的原因是,在某种程度上,数据和代码(在内存中以数字/代码表示)的冯·诺依曼体系结构以相同的方式存储在内存中。

我迅速重新编码了程序,以便将这两个程序的下一条指令写为零,即编辑自己的程序和相对的程序。 由于我的程序在运行时必须位于内存中,因此在其他程序运行时它将被换出内存。 如果我已经覆盖了相反程序的下一条指令,那么该程序唯一要做的就是“结束”。

代码能自我意识吗?来自麻省理工学院学习计算机科学的思考_第4张图片

程序可以覆盖自身吗? 事实证明,这并不难!

与我最初的担心相反,我的程序不会杀死自己,我正在覆盖PC(或程序计数器),或者说是PC指向内存中的位置。 下次我的程序运行时,PC将递增,而下一条指令将开始运行。 其他程序,其中有不被运行我的程序运行的时候,就必然在PC指出,为自己的位置回暖。

通过这样做,用两条简单的指令,我的程序就尽可能地高效,而其余的都是肉汁。我可以使下一条指令添加“ rizkill wins”之类的东西。

实际上,这个小程序(是的,我称其为“ rizkill”)在编程竞赛中胜出,杀死了我所有同学中差劲的程序,这些人都花了2条以上的汇编代码指令来实现任何目标。

后来,为了助教,TA决定对研究生TA的课程实施rizkill,尽管它轻而易举地击败了大多数TA的课程,但还是有一个聪明的TA曾使用过同样的技巧,因此这是一种折衷,程序将赢得任何给定的会话(机敏的观察者会注意到,它仅取决于首先运行哪个进程)。

我被宣布为本科生的优胜者,并获得了斯蒂芬·列维(Stephen Levy)签名的黑客副本作为我的奖赏(不幸的是,它是由我们的教授斯蒂芬·沃德(Stephen Ward)签名的,尽管我更愿意由作者斯蒂芬·列维(Stephen Levy)签名,但这完全是另一个问题!)。

Self and Copies of Self

您可能会争辩说,我的程序的两个版本都不是真正的“自我意识”,尽管我会争辩说这绝对是“自我参考”,这不是完全一样的事情。 但是,这肯定让我考虑了其他具有“自我参照”功能的软件程序,这是否是“自我意识”的先驱?

您可以编写一个软件来做盖洛普(Gallup)镜像测试的虚拟版本,看看代码是否可以识别出放置在其上的标记。 怎么样? 实际上,由于我已经编写了一个自我修改程序,因此可以使用各种虚拟“镜像”对该程序使用各种技术来“检查自身”以查看其是否已被修改。

最有可能的是,校验和或某种类型可以解决问题,或者可以在某处复制自己的代码以进行比较。

计算机科学中有一类程序会“引用”他们自己的代码-木马是自我复制的程序,吐出其源代码的一个版本作为其唯一输出。 稍后我们将详细讨论的道格拉斯·霍夫施塔特(Douglas Hofstadter)在他的经典著作《哥德尔·埃舍尔·巴赫》(GödelEscher Bach,简称GEB)中定义了“ quine”一词,尽管无处不在的冯·诺依曼是第一个谈论此类计划的人。

代码能自我意识吗?来自麻省理工学院学习计算机科学的思考_第5张图片

程序可以输出自己的副本吗?

程序员将认识到“ quine”将是一个源代码包含在可以打印出的字符串中的程序。

维基百科和其他地方有很多奎因的例子。 一个简单的用python编写的代码是:(有关更多信息,请参见https://en.wikipedia.org/wiki/Quine_(computing) )

s ='s = %r \ n print(s %% s)'
print(s%s)

当然,在简单的方法上有很多变化。大多数复制自己的计算机程序将复制其目标代码,而这些目标代码不需要在字符串中提供源代码(如上所示)或对代码进行反编译。 这称为变态代码,它给出自身的“版本”,通常是目标代码版本。

我们都知道自我复制程序,它们通常被称为“病毒”,因为它们的行为是在您不知情的情况下将自己复制到您的计算机上(以及其他计算机上)。

计算机程序的镜像测试

是否可以编写可以通过Mirror测试的“虚拟版本”的程序? 即,如果您在程序上标记(以某种方式更改),那么您是否有一个程序会意识到这一点?

就像编写一个基本上在每个语句中都执行此操作的程序一样,在像我们正在运行的多程序环境中执行此操作要困难得多,但是现在让我们忽略它。

代码能自我意识吗?来自麻省理工学院学习计算机科学的思考_第6张图片

看着镜子里的机器人

一些简单的伪代码:

//在程序开始时
Checksum = X;(这可能需要存储在磁盘上的某个地方,让我们忽略程序如何计算自己的校验和)
//之后,也许经常这样:
Y = getCurrentChecksum()
如果(X!= Y)
打印(“我已经被修改了……。我的漂亮!”)
其他
…//继续执行程序打算执行的任何操作

校验和主要用于错误检测,尤其是在下载大量数据或代码时。 实际上,如果您下载Microsoft Office,例如,您可能正在下载超过1 Gig的数据-它经常被分解成较小的块,每个块都有校验和。

好的,从理论上讲,您可以编写一个了解其“过去状态”以及是否已对其进行修改的程序,但这似乎并没有达到我们在“人工”中对其进行思考的方式的“自我意识”水平情报。

递归,自引用和上下文

让我们回到眼前的问题:这些程序中的任何一个在某种程度上“意识到”自己吗? 还是仅仅是“自我参照”?

回到麻省理工学院的计算机科学教育后,我意识到我们很早就开始学习自参考程序。

事实证明,与许多其他大学不同,我们在麻省理工学院(MIT)的入门级软件课(6.001)(“计算机程序的结构和解释”)使用Scheme语言(LISP的一种版本),该语言几乎没有语法/结构或与大多数编程语言相比,完全没有定义命令。

代码能自我意识吗?来自麻省理工学院学习计算机科学的思考_第7张图片

不像新生在其他大学学习使用C之类的实用语言编程的人(当时是将来的Java,后来是Java),我们一开始就以几乎没有人使用的语言编写大量的递归程序。在我25年的编程生涯中不再需要!)。

尽管如此,这种结构上的不足使得必须尽早了解递归。

递归是程序调用自己完成较小版本的问题时的一种,是计算机程序中使用的相当标准的技术。 假设您想提高3的4次方。 您可以将其表示为3 * 3 * 3 * 3,但也可以将其表示为3 *(将3提升到3rdpower)。 括号中是完全相同问题的较小版本。 那个较小的问题可以表示为3 *(较小的版本,或者3升为2次幂)。

在伪代码中,您可以使用以下代码段作为一种简单的递归方法来计算某些基值的幂或指数:

函数recursiveExponent(int base,int exponent)返回int
{如果(指数== 0)
返回1;
其他
返回(recurviseExponent(base,exponent-1);
}

程序是否意识到自身? 很明显它在自称,但是它知道它在自称吗? 还是只是调用另一个程序?

递归在循环中工作,并递归直到达到返回具体值的基本级别。 在这种情况下,第0次幂的任何数字都是1,因此这是递归的“基础”。

如果我们去掉了代码的底线,我们只会有一个调用自身的函数:

函数recursiveExponent(int base,int exponent)返回int
返回(recursiveExponent(base,exponent-1);

您将有一个无限循环-指数将继续下降成为负整数。 抛开数学解释,在大多数计算机上,这不会永远持续下去,具体取决于您所运行的操作系统,它可能会达到为程序分配多少内存的限制。 它将得到可怕的堆栈“溢出”错误-这是当今程序运行方式的构件,该程序允许递归,即逻辑“堆栈”(这是数据结构的后进先出类型)。 每次调用新函数(无论是否递归)时,都会在堆栈上创建一个新的“对象”,该对象成为函数特定运行的“上下文”。

如果递归程序永远继续下去,它将最终耗尽堆栈所需的所有内存。

这些特定于递归程序,因为使用循环编写的同一程序的迭代版本不会继续在堆栈上创建内存,而只会继续运行。

相同程序的迭代版本可能会永久运行(或至少达到指数),但不会进行任何自引用:

函数iterativeExponent(base,exponent)
{int结果= 1;
而(i = 0; I <=指数; i ++)
结果=结果*基数;
}

如果“指数”过大,最终可能会导致问题,因为“结果”的值随每次迭代而增加,实际上,变量具有大小限制。

但是我离题了。 回到递归程序。

但是,假设我们将程序更改为调用其他函数(可能具有类似的代码库):

函数recursiveExponent(int base,int exponent)返回int
{如果(指数== 0)
返回1;
其他
返回(recurviseExponent2(base,exponent-1);
}

现在,该程序不是在调用自身,而是在调用另一个函数。

程序知道区别吗?

现在您可以说这既不是自我意识也不是自我参照。 仍然,第二个程序可能会调用第一个程序,然后您有一个(稍微)更复杂的递归系统,该系统仍然具有自引用功能,因此问题仍然存在-在某种程度上自引用的系统是否了解自身?

转移注意力:递归象棋以及我(最终)如何解决“八皇后”问题

递归是一种非常强大的技术,尽管它比迭代方法要消耗更多的资源,并且事实证明,大多数问题可以在您真正考虑时使用这两种方法来解决。

递归的真正力量在于,它教导了较小问题的“抽象”或“黑匣子”概念-而不是找出整个问题,而是让“其他人”找出问题的一部分,然后简单地“添加”您只需通过简单的计算就可以将解决方案的一部分”。

使其递归,而不仅仅是打电话给别人来解决较小的问题的原因在于,递归中“别人”实际上只是你自己。 这就像克隆自己并要求自己的这个“较小版本”解决较小的问题并将解决方案交给您一样。

当我在高中学习编程时,我并不真正理解递归或使用它。 这主要是因为我使用的是Applesoft BASIC,虽然循环很容易,并且早在那时就已开始讲授,但使用过程语言而非基于对象的语言进行递归并不是那么容易。

我的朋友古斯(与我一起参加科学奥林匹克运动会的计算机编程团队),我偶然发现了一本可以通过计算机程序解决的数学问题书(这是预互联网,我们必须去买这本书,或者从图书馆中获得!)。 他们的练习之一是以下问题,称为8皇后问题:它困扰了我们快速增长但仍不成熟的计算机编程技能:

棋盘有8行和8列(64个正方形)-可以在此棋盘上安排8个皇后,以便没有一个皇后能够杀死任何其他皇后。 编写程序以查找一种配置。 额外的功劳:编写一个程序来查找每个配置。

今天,您可以在Internet上快速搜索各种解决方案,整个Wikipedia页面专门讨论8个皇后问题:“这是所谓的简单但非平凡问题的一个很好的例子”。 蛮力法显示有4,426,165,368( 即 64 C 8 )可能的八个皇后区盲位。 该问题有92种解决方案,可以通过排除在同一行或同一列上有女王/王后的配置来简化,因此,从理论上讲,您应该能够到达那里而不必经历非常单一的可能布置。

代码能自我意识吗?来自麻省理工学院学习计算机科学的思考_第8张图片

8皇后问题的解决方案

但是在那些日子里,我们无法在Wikipedia上查找它,而不得不深入研究。起初看起来这并不是一个特别复杂的问题。您只需在一个循环中编写一个循环就可以解决该问题。 还是我们想到了。 当Gus和我尝试使用简单的迭代来解决它时,它很快就变得毛茸茸了,或者变得计算量大,以至于使用Applesoft BASIC永远无法解决该问题。 我们不知道上下文和堆栈框架是递归的重要组成部分。

那个秋天我去麻省理工学院,上了第一门编程课,即6.001(然后递归阴阳)时,我意识到8皇后问题可以用同一个问题的较小版本来解决:8x8可以布置棋盘,以便可以在棋盘上放置7个皇后,这样没人可以互相攻击。 如果可以解决该问题,则只需将8thqueen放在空的列/行中。 当然,可以通过解决6个皇后问题来同样地解决7个皇后问题,依此类推。

我对执行此操作时会产生的复杂性有些模糊,但我确实记得那个夏天回家并向Gus展示了我的解决方案(该解决方案仅解决了找到一个配置而不是全部配置的问题),我们都深信递归是解决计算机科学中任何问题的最有效方法! (注意:Gus的名字已更改为保护无辜者)。

原文链接: https://hackernoon.com/can-code-be-self-aware-musings-on-studying-computer-science-at-mit-part-i-446d98efb743

你可能感兴趣的:(代码能自我意识吗?来自麻省理工学院学习计算机科学的思考)