NetLogo多智能体仿真入门:构建社会网络信息扩散模型
2026/9/8 9:04:23 网站建设 项目流程

先说个背景。我之前在带本科生做社会网络课题时,很多人一听到"仿真建模"四个字就头皮发麻,觉得必须得先啃完《概率论》《图论》《程序设计》三座大山才有资格动手。但NetLogo这个软件,完全是另一条路——它的设计初衷就是让非计算机背景的研究者也能快速搭建多智能体模型。前两篇我讲了环境配置和界面基本操作,这一篇直接进入编程环节。我会用一个完整的社交网络信息扩散模型串起全部知识点,你跟着走一遍,基本就能掌握NetLogo的核心编程思路,后面再自己加需求也有底气了。

1. 先搞清楚NetLogo里的"三层世界观"

1.1 海龟、瓦片、观察者到底各管什么

NetLogo的编程模型脱胎于Logo语言,但加了一层非常关键的抽象:整个仿真世界被分成了三类主体,分别叫turtles(海龟/智能体)、patches(瓦片/网格)和observer(观察者)。

turtles是"能动"的主体。在社会网络仿真里,一个人、一家公司、一只动物,都可以抽象成一只turtle。它们在二维世界里可以移动、变色、创建连接。每个turtle自带一些内置属性,比如xcorycor是坐标,color是颜色,size是大小,heading是朝向。这些你不用自己定义,直接用就行。

patches是静态的格子。整个仿真世界被切分成网格,每个格子就是一个patch。它们的坐标是整数,比如(0, 0)(1, 1)。在社会网络模型里,patches一般用来表示空间环境——比如某个区域的资源量、污染程度、人口密度。如果你研究的网络跟地理位置没关系,那patches基本就是背景板,但千万别以为它没用,后面做空间扩展时你就会回来找它。

observer是"上帝之手"。它不对应任何仿真对象,负责全局调度。你写的setupgo这些过程,本质上都是observer在执行;ask turtles这个命令,就是observer在向所有turtle下发指令。

用一句大白话总结:turtles是演员,patches是舞台,observer是导演。理解了这三层权限边界,你的代码逻辑就会清晰得多。

1.2 从命令中心到代码编辑器

NetLogo的界面有三大页签:Interface(交互界面)、Info(模型说明)、Code(代码编辑)。很多人上手第一件事就是跑去Code页签写代码,写完之后一头雾水,不知道发生了什么。我的建议是:先在Interface底部的Command Center(命令中心)里敲指令,边敲边看世界变化,等逻辑理顺了再搬到Code编辑器里组织成过程。

举个例子,你可以在Command Center里输入:

create-turtles 10

回车之后,世界中间立刻出现10只小海龟,随机朝向和颜色。再看它们的坐标:

show [xcor] of turtles

这一步会直接在命令中心打印出10个坐标值。这种"边输边看"的交互方式,是NetLogo对新手最友好的地方。等你对每个命令的效果心里有数了,再去Code页签写完整的过程。

2. NetLogo编程语法:够用就好的那套核心

2.1 变量:全局、个体、局部三层

NetLogo的变量系统是理解整个编程的关键,因为它跟"谁在持有这个变量"强相关。

首先是全局变量,用globals关键字在代码顶部声明。它在整个仿真运行期间都存在,任何主体都能访问。比如后面案例里的infected-count,就是全局变量。

globals [ infected-count ]

其次是个体变量,也叫主体自有变量。每个turtle可以有自己的turtles-own,每个patch可以有patches-own。比如社会网络里每个人的感染状态、免疫状态,都属于个体变量。

turtles-own [ infected? immunity? ]

注意NetLogo的变量命名规则:如果名字里要表达布尔含义,习惯性在后面加个问号,比如infected?,读起来就是"已感染吗?",非常直观。

最后是局部变量,用let关键字在过程内部创建,只在当前过程内有效。它是临时变量,用来存中间计算结果,避免污染全局命名空间。

let total 0

有个新手特别容易踩的坑:在NetLogo里,你用set给变量赋值之前,这个变量必须先声明过。如果在turtles-own里没有声明,直接set age 20,运行时会直接报错"you can't set variable age"。

2.2 命令和报告器:一个执行动作,一个返回结果

NetLogo把程序元素分成两大类:command(命令)和reporter(报告器)。

命令是执行动作的,比如create-turtlesfdsetask,它们执行完之后不返回任何值。报告器则是计算并返回一个值,比如count turtlesrandom 10one-of turtles

这个区分的价值在于:你不能把一个命令写在一个需要值的位置。比如写let x fd 1就是错的,因为fd命令不返回值。反过来,show count turtles是可以的,因为count turtles是报告器。

在实际使用中,最常见的组合模式是:

ask turtles with [ color = red ] [ set size 2 ]

这句的意思是:observer筛选出所有color = red的turtle,让它们每个都执行set size 2with是一个筛选报告器,它会过滤出一个子集。

2.3 流程控制:if、ask和循环

NetLogo里最重要的流程控制不是for,而是ask。因为仿真本质上就是"多个主体并行行动",所以你得用ask把指令下发到每个主体。ask后面跟的主体集合可以是turtlespatches,也可以是某个筛选出来的子集。

条件判断用ififelse

if infected? [ set color red ] ifelse infected? [ set color red ] [ set color blue ]

ifelse跟一般语言的if-else一样,但注意括号的位置和两个分支的顺序,中间用方括号分隔。

循环方面,最常用的是loop配合stop或者repeat,以及列表遍历用的foreach。不过在社会网络模型里,循环往往被go过程取代了——每个tick就是一个时间步,go过程会被反复调用,这本身就是一种循环。

3. 社会网络仿真里的核心编程模式

3.1 用turtles代表节点,用links代表关系

社会网络仿真的第一步,是把抽象的关系网络翻译成NetLogo的实体。节点好办,用turtles;关系需要用到NetLogo的另一类主体——links(连接)。

links是NetLogo里非常巧妙的设计。它也是一种主体,但它的存在必须依附于两个turtle。你创建一条链接时,实际上是"从某个turtle出发,连接到另一个turtle"。另一个关键点是,链接分有向(directed-links)和无向(undirected-links)两种。如果你研究的是微博关注关系,那应该用有向链接;如果研究的是微信好友,那应该是无向链接。

ask turtle 0 [ create-link-with turtle 1 ]

这行代码会在turtle 0和turtle 1之间建立一条无向链接。如果turtle 0已经和turtle 1有链接,再次执行会报错,所以创建之前通常要检查:

if not link-neighbor? turtle 1 [ create-link-with turtle 1 ]

有了links,你就能用网络分析的本体了。link-neighbors会返回和某个turtle直接相连的所有turtle,degree(度)、count link-neighbors这些指标都能直接算出来。

3.2 建网络的三板斧:随机连、环形连、按规则连

入门阶段,你不需要自己从零写复杂的网络生成算法。NetLogo自带的nw扩展就是干这个的,但我建议你第一遍先手动建一个简单网络,理解底层逻辑。

最常见的建网方法有三种。

第一种是随机连接:每个节点随机选择K个其他节点建立连接。这种网络接近ER随机图。

create-turtles 30 ask turtles [ create-links-with n-of 3 other turtles ]

n-of 3 other turtles表示从"除了自己以外的其他turtle"里随机抽3个。这种写法简洁,但有个小瑕疵:other turtles是排除了自己的,所以每个节点不会连到自己。

第二种是环形网络:把turtles排成一个圈,每个节点连接前后各K个邻居。环形网络是研究小世界现象的基础。

create-turtles 30 layout-circle turtles max-pxcor - 1 ask turtles [ let my-id 0 ask other turtles [ if abs (my-id - [who] of self) <= 2 [ ; who是内置编号 if not link-neighbor? myself [ create-link-with myself ] ] ] ]

不过新手在这里容易绕晕,建议直接用nw扩展的generate-watts-strogatz函数。

第三种是按属性规则连接:比如每个新节点优先连接度高的节点(无标度网络的核心逻辑)。NetLogo自带的模型库里就有这个例子,你可以参考Model Library里的Preferential Attachment模型。

3.3 网络怎么演化:加点、删边、改权重

真实社会网络不是静态的。人会加入、退出,关系会建立、破裂。NetLogo里的演化逻辑通常放在go过程里,每个tick执行一次。

添加节点:

create-turtles 1 [ set color blue create-links-with n-of 2 turtles ]

删除节点:

ask one-of turtles [ ask my-links [ die ] die ]

注意删除节点的同时要记得删除它的链接,否则NetLogo会报错——链接必须依附于节点存在。

对链接做权重动态更新时,可以用links-own声明一个weight变量,然后每次tick根据某种规则更新。比如亲密度越高权重越大,或者信息越传播权重越衰减。

links-own [ weight ]

设置权重:

ask links [ set weight 1 ]

4. 实操案例:30人小圈子里的信息扩散模型

说了这么多理论,现在动手做一个完整的模型。场景设定:一个30人的小群体,每个人随机认识另外3个人(无向关系)。某天有一个人获得了一条"劲爆消息",每次单位时间他/她会以一定概率把消息告诉每个"认识的人",而刚听到消息的人下一轮也会继续传播。我们想观察:这个消息最终能传多远?速度快不快?

4.1 模型参数和界面控件的设计思路

打开NetLogo,在Interface页签里你需要拖入几个控件:

  • 一个setup按钮,关联到setup过程
  • 一个go按钮,关联到go过程,勾选"Forever"(无限重复执行)选项
  • 一个probability滑动条,范围0到100,默认30,控制传播概率
  • 一个monitor(监视器),显示当前感染人数
  • 一个plot(图),显示感染人数随时间的变化曲线

完成界面布局之后,点鼠标右键,每个控件都能设置关联的过程名和参数范围。这里的核心是probability滑动条的值可以直接在代码里用probability这个变量引用,不需要你额外声明。

4.2 setup过程的代码拆解

打开Code页签,先写setup

globals [ infected-count ] turtles-own [ infected? immunity? ] to setup clear-all reset-ticks set infected-count 0 create-turtles 30 [ set color blue set size 2 set infected? false set immunity? false ] ask turtles [ create-links-with n-of 3 other turtles ] layout-circle turtles max-pxcor - 1 ask one-of turtles [ set infected? true set color red set infected-count infected-count + 1 ] end

这段代码的逻辑链是:先清空世界,重置时钟,初始化全局计数;再创建30个turtles,设置初始属性;然后每个turtle随机生成3条无向链接;用layout-circle把节点均匀排成一个圆(这样看起来不乱);最后随机挑一个turtle设置为感染者。

这里有个细节值得提:layout-circle turtles max-pxcor - 1max-pxcor是世界的最大横坐标,默认大概是16,所以圆的半径用15,刚好把节点排在世界边缘以内。如果你的世界尺寸不同,布局效果会不一样,但规则是一样的——半径留白一点,别让节点贴边。

4.3 go过程:每一轮传播逻辑怎么写

go过程是这个模型的心脏,它描述了每个tick里发生的所有事情:

to go ask turtles with [infected?] [ ask link-neighbors [ if not infected? and not immunity? [ if random-float 100 < probability [ set infected? true set color red ] ] ] ] set infected-count count turtles with [infected?] tick end

我用一句话先概括执行流程:遍历当前所有感染者,对每个感染者的每个邻居,以probability%的概率把对方也感染。

很多新手读到这里会卡在一个地方:ask turtles with [infected?]这一步,如果某个turtle在这一轮里被感染了,它会不会"立刻"在本轮里继续传播一轮?答案是:不会。因为NetLogo的ask是先确定好主体集合,再逐一执行。即使执行过程中某个turtle的infected?变成了true,它也不会被加入本轮集合中。这个特性保证了模型的时间步逻辑不会混乱。

更新感染人数用了count报告器配合with筛选。注意这里是先算完所有传染操作,再统一统计人数,所以计数是准确的当前tick结束后的状态。

tick是NetLogo的时钟,每执行一次go调用,时钟加一。如果你的界面里放了plot,可以在这个位置更新图的数据:

plot infected-count

4.4 运行模型、观察现象和参数调优

点击setup,再点go,你应该能看到红色节点像涟漪一样在圆圈里扩散。

如果感染曲线每次都快速达到100%,说明传播概率设太高了,没有对比价值;如果曲线几乎不增长,说明概率太低或初始节点不够。这里没有标准答案,关键是你作为建模者,要能从输出中读到结构信息。

我实测下来,值得观察的现象有三个。

第一,感染规模增长大多不是线性的,而是S型曲线。先慢(感染者少,接触面窄),中间陡增(感染节点多了,扩散面广),后期平缓(大部分节点要么感染过、要么与感染节点隔绝)。

第二,网络度对传播速度有明显影响。把create-links-with n-of 3 other turtles里的3改成5,再运行一次,你会发现扩散速度明显加快。这是因为每个节点的平均度提高了,网络连通性更好。

第三,如果把网络变成多个互不连通的"孤岛",传播就会局部化。你可以在创建链接时限制在同一"群组"内,观察信息永远传不到其他群组的效果。这种"桥接"断裂是真实社会中非常常见的传播阻断原因。

5. 常见问题与调试技巧实录

5.1 新手最容易翻车的五类报错

我在带学生做NetLogo实验时,总结了五类出现频率最高的报错场景,这里直接给你一个速查表。

报错场景原因解决办法
Nothing named SIZE has been defined变量名拼写错误或未声明检查turtles-own声明和赋值语句,NetLogo变量名区分大小写
Requested random number is 0random的参数传了0,报错检查是不是把random-float写成了random,或者检查参数是否为正整数
create-link-with: only two turtles can be in a link试图创建重复链接或自链接创建前加if not link-neighbor?判断
ask: expected 2 inputsask语法写错,缺了方括号ask turtles [ ... ],方括号不能省
Can't find turtle 5引用了一个已经被die的turtle检查删除节点时是否同步删除了相关链接

这里面我特别想强调变量名的问题。NetLogo不区分sizeSize,但你声明了Size却用了size就出问题。有一次我帮学生看代码,查了半天,最后发现他把turtles-own里写的变量名是Infected?,代码里用的是小写infected?,NetLogo直接当成两个变量,初始值全变成false,逻辑判断一团糟。

5.2 调试三板斧:show、watch、单步执行

NetLogo没有传统意义上的断点调试器,但它的调试手段比很多人想的要实用。

第一板斧是show。在命令中心输入show count turtles,马上能看到数字。如果你想在一段过程中临时输出某个值,可以直接在代码里加show语句,运行后会在命令中心打印出来。

第二板斧是watch。在命令中心输入watch turtle 3,界面视图会锁定并高亮显示编号为3的turtle,你就能像个跟踪器一样观察它每一帧的行为。配合watch-me放在局部过程里,可以对满足特定条件的实体做动态追踪。

第三板斧是单步执行。在Interface页签的go按钮上,鼠标右键可以勾选"Step once"或者不勾选"Forever"。这样每点一下按钮只执行一个tick,方便你逐步观察状态变化。如果模型跑得太快看不清,也可以在go过程开头加一句wait 0.5,让它慢半拍。

5.3 模型规模大了怎么办

NetLogo在几千个智能体时运行还算流畅,但一旦超过几万,速度就会有明显下降。社会网络仿真如果节点数上了万,需要注意三件事。

第一,减少不必要的ask重复遍历。比如每次tick里都用ask turtles [ ... ]去扫描所有节点,即使大部分节点状态没变化,也会消耗时间。可以改用ask turtles with [infected?],让筛选发生在遍历之前。

第二,尽量少用layout类命令。像layout-spring这类力导向布局算法,本质上是迭代计算,每隔几帧调用一次可以,但如果每tick都调用,仿真实时会卡到难以接受。

第三,使用NetLogo自带的"BehaviorSpace"工具做参数扫描时,注意设置合理的迭代次数。BehavioSpace可以自动化跑多组不同参数的仿真并导出结果,但如果你设置的范围过大,比如让probability从0到100,步长1,再跑1000个tick,一次实验就要几分钟了。

写在最后的一点体会

在这套系列文章写到这里之前,我一直在想怎么把NetLogo这个工具讲得不像说明书。说真的,它最打动我的地方不是语法多高级、性能多强,而是它让"建模思维"变成了一种近乎直觉的东西。你不需要先背一百个函数,只需要想清楚:这世界里有谁、它们之间有什么关系、每一刻发生什么变化。然后把这个想法翻译成turtles、links和ask,模型就跑起来了。信息扩散、谣言传播、舆论演化、社交网络成长,这些听起来高大上的概念,在NetLogo里都是半小时能搭出雏形的事。如果你顺着这篇的思路自己动手跑通了这个模型,建议你再往前走一步:把节点换成"个体态度偏好",把传播规则换成"同质化吸引",你会发现同一套骨架能装很多不同的血肉。那才是NetLogo真正好玩的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询