當前位置:
首頁 > 知識 > 基於 Agent 的模型入門:Python 實現隔離模擬

基於 Agent 的模型入門:Python 實現隔離模擬

(點擊

上方藍字

,快速關注我們)




編譯:伯樂在線 - Ree Ray


如有好文章投稿,請點擊 → 這裡了解詳情




2005 年諾貝爾經濟學獎得主托馬斯·謝林(Thomas Schelling)在上世紀 70 年代就紐約的人種居住分布得出了著名的 Schelling segregation model,這是一個 ABM(agent-based model),當年謝林只能通過鉛筆在紙上進行模擬,而這次則利用 Python 進行模擬實現這個模型。




在計算機科學中,基於 Agent 的模型(agent-based models)被用來評估獨立(autonomous)agent(諸如個體、群組或物體)在整個系統中的影響。這個強大的分析工具常被用在實驗難以開展或者費用太高的時候。在社會科學,計算機科學,經濟學和商務各領域,這個模型都有著相當廣泛的應用。



在本文中,我會向你介紹用基於 Agent 的模型理解複雜現象的威力。為此,我們會用到一些 Python,社會學的案例分析和 Schelling 模型。




1. 案例分析




如果你觀察多民族(multi-ethnic)混居城市的種族(racial)分布,你會對不可思議的種族隔離感到驚訝。舉個例子,下面是美國人口普查局(US Census)用種族和顏色對應標記的紐約市地圖。種族隔離清晰可見。




許多人會從這個現象中認定人是偏隘的(intolerant),不願與其它種族比鄰而居。然而進一步看,會發現細微的差別。2005 年的諾貝爾經濟學獎得主托馬斯·謝林(Thomas Schelling)在上世紀七十年代,就對這方面非常感興趣,並建立了一個基於 Agent 的模型——「Schelling 隔離模型」的來解釋這種現象。藉助這個極其簡單的模型,Schelling 會告訴我們,宏觀所見並非微觀所為(What』s going down)。




我們會藉助 Schelling 模型模擬一些模擬來深刻理解隔離現象。






2. Schelling 隔離模型:設置和定義




基於 Agent 的模型需要三個參數:1)Agents,2)行為(規則)和 3)總體層面(aggregate level)的評估。在 Schelling 模型中,Agents 是市民,行為則是基於相似比(similarity ratio )的搬遷,而總體評估評估就是相似比。




假設城市有 n 個種族。我們用唯一的顏色來標識他們,並用網格來代表城市,每個單元格則是一間房。要麼是空房子,要麼有居民,且數量為 1。如果房子是空的,我們用白色標識。如果有居民,我們用此人的種群顏色來標識。我們把每個人周邊房屋(上下左右、左上右上、左下右下)定義為鄰居。



Schelling 的目的是想測試當居民更傾向於選擇同種族的鄰居(甚至多種族)時會如果發生什麼。如果同種族鄰居的比例上升到確定閾值(稱之為相似性閾值(Similarity Threshold)),那麼我們認為這個人已經滿意(satisfied)了。如果還沒有,就是不滿意。




Schelling 的模擬如下。首先我們將人隨機分配到城裡並留空一些房子。對於每個居民,我們都會檢查他(她)是否滿意。如果滿意,我們什麼也不做。但如果不滿意,我們把他分配到空房子。模擬經過幾次迭代後,我們會觀察最終的種族分布。




3. Schelling 模型的 Python 實現




早在上世紀 70 年代,Schelling 就用鉛筆和硬幣在紙上完成了他的模擬。我們現在則用 Python 來完成相同的模擬。




為了模擬模擬,我們首先導入一些必要的庫。除了 Matplotlib 以外,其它庫都是 Python 默認安裝的。




import

matplotlib

.

pyplot

as

plt


import

itertools


import

random


import

copy




接下來,定義名為 Schelling 的類,涉及到 6 個參數:城市的寬和高,空房子的比例,相似性閾值,迭代數和種族數。我們在這個類中定義了 4 個方法:populate,is_unsatisfied,update,move_to_empty, 還有 plot)。




class

Schelling

:


def

__init__

(

self

,

width

,

height

,

empty_ratio

,

similarity_threshold

,

n_iterations

,

races

=

2

)

:


self

.

width

=

width


self

.

height

=

height


self

.

races

=

races


self

.

empty_ratio

=

empty_ratio


self

.

similarity_threshold

=

similarity_threshold


self

.

n_iterations

=

n_iterations


self

.

empty_houses

=

[]


self

.

agents

=

{}



def

populate

(

self

)

:


....



def

is_unsatisfied

(

self

,

x

,

y

)

:


....



def

update

(

self

)

:


....



def

move_to_empty

(

self

,

x

,

y

)

:


....



def

plot

(

self

)

:


....




poplate 方法被用在模擬的開頭,這個方法將居民隨機分配在網格上。





def

populate

(

self

)

:


self

.

all_houses

=

list

(

itertools

.

product

(

range

(

self

.

width

),

range

(

self

.

height

)))


random

.

shuffle

(

self

.

all_houses

)



self

.

n_empty

=

int

(

self

.

empty_ratio

*

len

(

self

.

all_houses

)

)


self

.

empty_houses

=

self

.

all_houses

[

:

self

.

n_empty

]



self

.

remaining_houses

=

self

.

all_houses

[

self

.

n_empty

:

]


houses_by_race

=

[

self

.

remaining_houses

[

i

::

self

.

races

]

for

i

in

range

(

self

.

races

)]


for

i

in

range

(

self

.

races

)

:


# 為每個種族創建 agent


self

.

agents

=

dict

(


self

.

agents

.

items

()

+


dict

(

zip

(

houses_by_race

[

i

],

[

i

+

1

]

*

len

(

houses_by_race

[

i

]))).

items

()




is_unsatisfied 方法把房屋的 (x, y) 坐標作為傳入參數,查看同種群鄰居的比例,如果比理想閾值(happiness threshold)高則返回 True,否則返回 False。





def

is_unsatisfied

(

self

,

x

,

y

)

:



race

=

self

.

agents

[(

x

,

y

)]


count_similar

=

0


count_different

=

0



if

x

>

0

and

y

>

0

and

(

x

-

1

,

y

-

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

-

1

,

y

-

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

y

>

0

and

(

x

,

y

-

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

,

y

-

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

<

(

self

.

width

-

1

)

and

y

>

0

and

(

x

+

1

,

y

-

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

+

1

,

y

-

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

>

0

and

(

x

-

1

,

y

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

-

1

,

y

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

<

(

self

.

width

-

1

)

and

(

x

+

1

,

y

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

+

1

,

y

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

>

0

and

y

<

(

self

.

height

-

1

)

and

(

x

-

1

,

y

+

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

-

1

,

y

+

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

>

0

and

y

<

(

self

.

height

-

1

)

and

(

x

,

y

+

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

,

y

+

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

<

(

self

.

width

-

1

)

and

y

<

(

self

.

height

-

1

)

and

(

x

+

1

,

y

+

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

+

1

,

y

+

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1



if

(

count_similar

+

count_different

)

==

0

:


return

False


else

:


return

float

(

count_similar

)

/

(

count_similar

+

count_different

)

<

self

.

happy

_

threshold




update 方法將查看網格上的居民是否尚未滿意,如果尚未滿意,將隨機把此人分配到空房子中。並模擬 n_iterations 次。





def

update

(

self

)

:


for

i

in

range

(

self

.

n_iterations

)

:


self

.

old_agents

=

copy

.

deepcopy

(

self

.

agents

)


n_changes

=

0


for

agent

in

self

.

old_agents

:


if

self

.

is_unhappy

(

agent

[

0

],

agent

[

1

])

:


agent_race

=

self

.

agents

[

agent

]


empty_house

=

random

.

choice

(

self

.

empty_houses

)


self

.

agents

[

empty_house

]

=

agent_race


del

self

.

agents

[

agent

]


self

.

empty_houses

.

remove

(

empty_house

)


self

.

empty_houses

.

append

(

agent

)


n_changes

+=

1


print

n_changes


if

n_changes

==

0

:


break




move_to_empty 方法把房子坐標(x, y)作為傳入參數,並將 (x, y) 房間內的居民遷入空房子。這個方法被 update 方法調用,會把尚不滿意的人遷入空房子。





def

move_to_empty

(

self

,

x

,

y

)

:


race

=

self

.

agents

[(

x

,

y

)]


empty_house

=

random

.

choice

(

self

.

empty_houses

)


self

.

updated_agents

[

empty_house

]

=

race


del

self

.

updated_agents

[(

x

,

y

)]


self

.

empty_houses

.

remove

(

empty_house

)


self

.

empty_houses

.

append

((

x

,

y

))




plot 方法用來繪製整個城市和居民。我們隨時可以調用這個方法來了解城市的居民分布。這個方法有兩個傳入參數:title 和 file_name。





def

plot

(

self

,

title

,

file_name

)

:


fig

,

ax

=

plt

.

subplots

()


# 如果要進行超過 7 種顏色的模擬,你應該相應地進行設置


agent_colors

=

{

1

:

"b"

,

2

:

"r"

,

3

:

"g"

,

4

:

"c"

,

5

:

"m"

,

6

:

"y"

,

7

:

"k"

}


for

agent

in

self

.

agents

:


ax

.

scatter

(

agent

[

0

]

+

0.5

,

agent

[

1

]

+

0.5

,

color

=

agent_colors

[

self

.

agents

[

agent

]])



ax

.

set_title

(

title

,

fontsize

=

10

,

fontweight

=

"bold"

)


ax

.

set_xlim

([

0

,

self

.

width

])


ax

.

set_ylim

([

0

,

self

.

height

])


ax

.

set_xticks

([])


ax

.

set_yticks

([])


plt

.

savefig

(

file_name

)




4. 模擬




現在我們實現了 Schelling 類,可以模擬模擬並繪製結果。我們會按照下面的需求(characteristics)進行三次模擬:







  • 寬 = 50,而高 = 50(包含 2500 間房子)



  • 30% 的空房子



  • 相似性閾值 = 30%(針對模擬 1),相似性閾值 = 50%(針對模擬 2),相似性閾值 = 80%(針對模擬 3)



  • 最大迭代數 = 500



  • 種族數量 = 2




創建並「填充」城市。





schelling_1

=

Schelling

(

50

,

50

,

0.3

,

0.3

,

500

,

2

)


schelling_1

.

populate

()



schelling_2

=

Schelling

(

50

,

50

,

0.3

,

0.5

,

500

,

2

)


schelling_2

.

populate

()



schelling_3

=

Schelling

(

50

,

50

,

0.3

,

0.8

,

500

,

2

)


schelling_3

.

populate

()




接下來,我們繪製初始階段的城市。注意,相似性閾值在城市的初始狀態不起作用。





schelling_1_1

.

plot

(

"Schelling Model with 2 colors: Initial State"

,

"schelling_2_initial.png"

)



下面我們運行 update 方法,繪製每個相似性閾值的最終分布。





schelling_1

.

update

()


schelling_2

.

update

()


schelling_3

.

update

()



schelling_1

.

plot

(

"Schelling Model with 2 colors: Final State with Similarity Threshold 30%"

,

"schelling_2_30_final.png"

)


schelling_2

.

plot

(

"Schelling Model with 2 colors: Final State with Similarity Threshold 50%"

,

"schelling_2_50_final.png"

)


schelling_3

.

plot

(

"Schelling Model with 2 colors: Final State with Similarity Threshold 80%"

,

"schelling_2_80_final.png"

)








觀察以上的繪圖,我們可以發現相似性閾值越高,城市的隔離度就越高。此外,我們還會發現即便相似性閾值很小,城市依舊會產生隔離。換言之,即使居民非常包容(tolerant)(相當於相似性閾值很小),還是會以隔離告終。我們可以總結出:宏觀所見並非微觀所為。




5. 測量隔離




以上模擬,我們只通過可視化來確認隔離發生。然而,我們卻沒有對隔離的計算進行定量評估。本節我們會定義這個評估標準,我們也會模擬一些模擬來確定理想閾值和隔離程度的關係。




首先在 Schelling 類中添加 calculate_similarity 方法。這個方法會計算每個 Agent 的相似性並得出均值。我們會用平均相似比評估隔離程度。





def

calculate_similarity

(

self

)

:


similarity

=

[]


for

agent

in

self

.

agents

:


count_similar

=

0


count_different

=

0


x

=

agent

[

0

]


y

=

agent

[

1

]


race

=

self

.

agents

[(

x

,

y

)]


if

x

>

0

and

y

>

0

and

(

x

-

1

,

y

-

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

-

1

,

y

-

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

y

>

0

and

(

x

,

y

-

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

,

y

-

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

<

(

self

.

width

-

1

)

and

y

>

0

and

(

x

+

1

,

y

-

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

+

1

,

y

-

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

>

0

and

(

x

-

1

,

y

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

-

1

,

y

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

<

(

self

.

width

-

1

)

and

(

x

+

1

,

y

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

+

1

,

y

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

>

0

and

y

<

(

self

.

height

-

1

)

and

(

x

-

1

,

y

+

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

-

1

,

y

+

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

>

0

and

y

<

(

self

.

height

-

1

)

and

(

x

,

y

+

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

,

y

+

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


if

x

<

(

self

.

width

-

1

)

and

y

<

(

self

.

height

-

1

)

and

(

x

+

1

,

y

+

1

)

not

in

self

.

empty_houses

:


if

self

.

agents

[(

x

+

1

,

y

+

1

)]

==

race

:


count_similar

+=

1


else

:


count_different

+=

1


try

:


similarity

.

append

(

float

(

count_similar

)

/

(

count_similar

+

count_different

))


except

:


similarity

.

append

(

1

)


return

sum

(

similarity

)

/

len

(

similarity

)




接下去,我們算出每個相似性閾值的平均相似比,並繪製出相似性閾值和相似比之間的關係。





similarity_threshold_ratio

=

{}


for

i

in

[

0

,

0.1

,

0.2

,

0.3

,

0.4

,

0.5

,

0.6

,

0.7

]

:


schelling

=

Schelling

(

50

,

50

,

0.3

,

i

,

500

,

2

)


schelling

.

populate

()


schelling

.

update

()


similarity_threshold_ratio

[

i

]

=

schelling

.

calculate_similarity

()



fig

,

ax

=

plt

.

subplots

()


plt

.

plot

(

similarity_threshold_ratio

.

keys

(),

similarity_threshold_ratio

.

values

(),

"ro"

)


ax

.

set_title

(

"Similarity Threshold vs. Mean Similarity Ratio"

,

fontsize

=

15

,

fontweight

=

"bold"

)


ax

.

set_xlim

([

0

,

1

])


ax

.

set_ylim

([

0

,

1.1

])


ax

.

set_xlabel

(

"Similarity Threshold"

)


ax

.

set_ylabel

(

"Mean Similarity Ratio"

)


plt

.

savefig

(

"schelling_segregation_measure.png"

)





通過上圖,可以發現即便相似性閾值非常小,依然會得到很高的隔離度(由平均相似性評估)。舉個例子,相似閾值為 0.3,會得到 0.75 的平均相似性。我們可以通過量化再次確定宏觀所見並非微觀所為。




6. 總結




在本文中,我們介紹了一個基於 Agent 的模型——「Schelling 隔離模型」,並用 Python 實現。這個十分簡單的模型幫助我們理解了非常複雜的現象:多民族城市中的種族隔離。我們可以發現城市種族的高度隔離不必解讀成個體層面的偏隘。




參考




https://www.coursera.org/course/modelthinking




看完本文有收穫?請轉

發分享給更多人


關注「P

ython開發者」,提升Python技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python 的精彩文章:

Python超級程序員使用的開發工具
編程語言中的超級英雄們:Python如同蝙蝠戰車,Ruby可比蝙蝠俠……
用Python攻破12306的最後一道防線,我看一遍就會了!
一件有趣的事:我用 Python 爬了爬自己的微信朋友
Python如何解析fasta格式,並儲存為字典?

TAG:Python |

您可能感興趣

Python yield與實現
基於Tensorflow實現DeepFM
Occipital推出MR創作工具Bridge Engine,實現密實3D映射
基於google protobuf的gRPC實現
用Pytorch 實現的 Capsule Network
用 greenlet 實現 Python 中的並發
AspectJ 框架 spring 實現 AOP?
Wormhole:一種基於Bitcoin Cash的智能合約實現方案
Machine Learning(一):基於 TensorFlow 實現寵物血統智能識別
CapsNet入門系列番外:基於TensorFlow實現膠囊網路
SAP Cloud for Customer Extensibility的設計與實現
深入 Spring Boot :實現對 Fat Jar jsp 的支持
SpringCloud如何實現Eureka集群、HA機制?
用Python 實現的大規模線性回歸、分類和排名庫——lightning
用TensorFlow 實現的模型集合
實現高速點擊的 Android Monkey 自動化工具 fastmonkey-代號 Maxim
ArrayList,LinkedList,Vector基本原理與實現
使用Tensorflow Object Detection API實現對象檢測
CentOS+OpenVZ+Vtonf實現Linux虛擬化
Andrew Ng經典機器學習課程的Python實現2