??在本章中,我們將看到關于狀態空間的信息是如何能夠防止算法在黑暗中跌跌撞撞的。
1.有信息的搜索策略
? ?我們要考慮的通用算法叫做“最佳優先搜索”(名字古老而不太準確)。它一般是將TREE-SEARCH和GRAPTH-SEARCH結合起來,它要擴展的節點是基于評價函數f(n)來決定的。
? 注意:評價函數有時不能真正的評價優劣,這將將搜索引入歧途。
? ?BEST-FIRST-SEARCH算法有一整個家族,其中不同的方法是使用不同的評價函數,這些算法的一個關鍵點為啟發函數(heuristic function),標記為h(n):
? h(n)=從節點n到目標節點的最低耗散路徑的耗散值。
1.1貪婪最佳優先搜索
? ?貪婪最佳優先搜索,從名字可以知道貪婪算法的思想。每次搜索擴展節點的時候,試圖擴展離目標節點最近的節點,因此它只用啟發函數f(n)=h(n)來評價節點。
? 例子:尋路問題--在一個國家中尋找由城市A到城市B最近的路線。當然,你有一副地圖。路線結果為從A城市到B城市所要經過的城市的順序列表。小路神馬就不考慮了。
? 貪婪最佳優先搜索首先會列出從A出發能直接到達的所有城市, 并計算每一個能直接到達的城市距離和B的距離,然后選擇最近的那個城市作為從A出發所到達的第一個城市,然后依次類推,直至到達城市B。
? 該搜索方法很簡單,簡單到明顯有很多缺點:不能保證最優,不具有完備性,如果不注意重復可能會出現震蕩的情況……但它畢竟讓我們邁出了第一步,壞的東西總比沒有的好。
1.2A*搜索:最小化總的估計解耗散
? 它把到達節點的耗散g(n)和從該節點到目標節點的耗散h(n)結合起來進行評價。
f(n) = g(n) + h(n)
? 該搜索算法很合理、很優秀。以至于我不知道該怎么摘錄《人工智能:一種現代方法》中的文字、或者利用自己的問題來形容。具體分析請見《人工智能:一種現代方法》中內容。
1.3儲存限制的啟發式搜索
? ?A*算法減少儲存/空間復雜度的辦法就是把迭代深入的思想用在啟發式搜索上。我們稱之為迭代深入A*算法或者IDA*算法。IDA*算法和典型的迭代深入算法最主要的區別就是,所用的截斷值不是搜索深度,而是耗散值f = g + h。IDA*對很多單位耗散問題都適用,但在實數耗散值的時候會出現困難(具體見《人工智能:一種現代方法》習題3.11)。以下考慮兩種儲存限制搜索算法,RBFS和MA*。
? function RECRSIVE-BEST-FIRST-SEARCH(problem) returns a solution, or failure
RBFS(problem,MAKE-NODE(INITIAL-STATE[problem]),∞)
function RBFS(problem,node,f_limit) returns a solution, or failure and a new f_cost limit
if GOAL-TEST[problem](state) then return node
successors <--- EXPAND(node,problem)
if successors is empty then return failure,∞
for each s in successors do
f[s] <--- max(g(s)+f(s),f[node])
repeat
best <--- the lowest f-value node in successors
if f[best] > f_limit then return failure, f[best]
alternative <--- the second lowest f-value among successors
result, f[best] <--- RBFS(problem,best,min(f_limit,alternative))
if result ≠failure then return result
? ? RBFS的特點可以說是“一步一回頭”,它會通過“alternative <--- the second lowest f-value among successors”來記錄與當前節點相平行層次的次優節點值,當向下搜索,當前最優點的后繼的評價函數值大于次優解時,放棄當前最優解,轉而向次優解。但上述代碼有一點問題,應該在最后“if result ≠failure then return result”之后加上“best <--- alternative, remove the lowest f-value node in successors”。將舍棄的最優解從successors中刪除,repeat才有意義,否則repeat會重復循環而失去了篩選最優解的意義。
? ? IDA*和RBFS的優點是空間復雜度底,缺點是……太低了……IDA*只保留一個數字:當前的f耗散值。RBFS保留的多一些。為了充分利用內存,由此改進出了兩個算法,MA*(儲存限制A*)和SMA*(簡化MA*)。SMA*和A*一樣會擴展最佳節點,直至內存放滿為止,但當內存放滿之后,它會像RBFS一樣遺忘一個最差的節點,并將它備份到父節點。當其他所有路徑比被遺忘的節點都差的時候,SMA*才會利用那個節點重新生成子樹。具體算法略(原書中也省略了,比較復雜,原文是“完整的算法在這里浮現太復雜了!”)。
2.啟發函數
? ? 如何設計一個啟發函數?
? ? 首先設計一個松弛問題,即降低了限制的問題。然后設計該松弛問題的最優解。該最優解的耗散值,是一個可采納的啟發函數。當設計了多個啟發函數時,可以采用如下公式:
? ? h(n) = max{h1(n),h2(n),...,hm(n)}
? ? 可采納的啟發式也可以通過給定問題的子問題的最優解的耗散得到。模式數據庫就是儲存每個可能的子問題的實例的精確耗散。這樣,每個搜索的開銷就分攤到了每個子問題上。這里也可以采取上述最大值原則。
? ? 總結:設計啟發函數方法有松弛問題法和子問題法(不限于這兩種),綜合啟發函數的方法有最大值法和平均數法(不限于這兩種)。
3.超越經典搜索算法
3.1 局部搜索和最優化問題
? ? 在搜索問題中,如果到目標的路徑無關緊要,那么將引出不關心路徑的算法。局部搜索算法從單獨的一個當前狀態(而不是多條路徑)出發,通常只移動到與之相鄰的狀態。典型情況下,搜索的路徑是不保留的,雖然局部搜索算法不是最優化的,但是它有兩個關鍵的優點:(1)只用很少的內存,通常是一個常數。它們經常能夠在不適合或者不能用系統化算法的很大的或者無限的狀態空間中找到合理的解。除了找到目標,局部搜索算法對于解決純粹的最優化問題很有用,其目標是根據一個目標函數找到最佳的狀態。
? 3.1.1爬山法搜索
function HILL-CLIMBING(problem) returns a state that is local maximum
inputs:problem, a problem
local variables:current, a node
neighbor,a node
current <--- MAKE-NODE(INITIAL-STATE[problem])
loop do
neighbor <--- a higest-valued successor of current
if VALUE[neighbor] =< VALUE(current) then return STATE[current]
current <--- neighbor
? 爬山搜索算法,節點的后繼中,如果存在目標函數的值大于當前節點的,移動到該后繼節點,如此循環。最終會返回一個局部最大值。該搜索算法的最大缺點是:局部最大值不一定是全局最大值。
3.1.2模擬退火算法
function SIMULATED-ANNEALING(problem,schedule) returns a solution state
inputs:problem, a problem
schedule, a mapping from time to "temperature"
local variables:current, a node
next, a node
T, a "temperature" controlling the probability of downward steps
current <--- MAKE-NODE(INITISL-STATE[problem])
for t <--- 1 to ∞ do
T <--- schedule[t]
if T = 0 then return current
next <--- a random selected successor of current
ΔE <--- VALUE[next] - VALUE[current]
if ΔE > 0 then current <--- next
else current <--- next only with probability e^(ΔE/T)
? ?模擬退火算法和爬山搜索算法很像,不同之處在于(1)模擬退火算法的后繼是隨機選取的,而爬山算法是選擇目標函數最大且大于當前目標節點值的。(2)模擬退火算法最終的返回的時刻由函數schedule[t]來決定,而不是由后繼達到某種狀態來決定的。(3)當后繼的目標函數值大于當前節點的目標函數值時,兩者都會將節點移動到后繼,但當后繼的值小于當前節點時,模擬退火算法按照一定的概率(e^(ΔE/T))來確定是否將節點移動到后繼節點。
3.1.3局部剪枝搜索
? ?局部剪枝搜索記錄k個狀態,而不是一個狀態。局部剪枝搜索由k個隨機的生成的狀態開始,每一個步都生成k個狀態的所有后繼,如果其中有一個是目標狀態,那么搜索就停止,否則從所有生成的后繼中選擇k個最佳的后繼,如此重復。
? ?注意:它和爬山算法很類似的一點就是,很容易將某一個一步較差的節點剪枝,即使這個節點的后繼表現很好。如果一個狀態的后繼很好,而其他k-1個狀態的后繼都不好,那么所有的機會就都集中到了這個節點后面,那么搜索就變得單一沒有多樣性。這和重新開始k次隨機搜索是不同的,局部剪枝搜索中信息在這k個并的搜索之間傳遞。這個算法的一個改進是隨機剪枝搜索。
3.1.4遺傳算法
function GENETIC-ALGORITHM( population, FITNESS-FN) returns an individual
inputs: population, a set of individuals
FITNESS-FN, a function that measures the fitness of an individual
repeat
new_population <--- empty set
for a <--- 1 to SIZE(popuitition) do
x RANDOM-SELECTION(populatio, FITNIESS-FN)
y RANDOM-SELECTION(popuLtion, FITNEss-FN)
child <--- REPRODUCE(x, y)
if (small random probability) then child <--- MUTATE( child)
add child to new_population
population <--- new_population
until some individual is fit enough, or enough time has elapsed
return the best individual in population, according to FITNESS-FN
-------------------------------------------------------------------------------------
function REPRODUCE(x, y) returns an individual
inputs: x,y, parent individuals
n <--- LENCTII(x)
c <--- random number from 1 to n
return APPEND(SUBSTRING(x, 1, c),SUBSTRING(y, c + 1, n))
3.2連續空間的局部搜索
(1)微分方程
(2)離散化
3.3非確定動作搜索
? ?對非確定動作,通過構造與-或樹來描述。以一個自動除塵器對含有多個房間的屋子進行打掃為例。該屋子中所有房間之間都是相鄰的,以九宮格排布,除塵器在房屋之間可以相互自由移動。除塵器可以以LEFT、RIGHT、UP、DOWN來行動。它可以執行SUCK來除塵。在某個房間里執行SUCK時,如果房間里有灰塵,SUCK動作將清除所有灰塵,偶爾還有可能將相鄰放假的灰塵一并清除;如果房間里沒有灰塵,那么它有時候會在房間里釋放一些灰塵。
? ?我們使用與-或樹來描述整個可能的清理過程。
? ?在房間里執行動作時,只是選擇一個動作,動作之間是互斥的,我們將搜索時只用考慮一個后繼的節點成為或節點;當執行一個動作時,可能會有兩種情況,我們在搜索時必須同時考慮兩種情況,我們將這種節點稱為與節點。具體參見《artificial intelligence a modern approach V3》page135。與節點域或節點組成搜索樹,稱之為與-或樹。
? ?與-或樹的一個搜索算法如下
function AND-OR-GRAM-SEARCH(problem) returns a conditional plan, or failure
OR-SEARCH(problem.INITIAL-STATE, problem,[ ])
function OR-SEARCH(state, problem, path) returns a conditional plan., or failure
if problem.GOAL-TEST(state) then return the empty plan
if state is on path then return failure
for each action in problem.ACTIONS(state) do
plan <--- AND-SEARCH(RESULTS(state, action), problem, [state | path])
if plan ≠ failure then return [action | plan]
return failure
function AND-SEARCH(states, problem, path) returns a conditional plan, or failure
for each s_i in states do
plan_i <--- OR-SEARCH(s_i, problem, path)
if plan_i = failure then return failure
return [if s_1 then plan_1, else if s_2 then plan_2, else ... if s_n-1 then plan_n-1, else plan_n]
3.4部分觀測
? ? 解決部分觀測的關鍵概念在于置信狀態,它是在給出動作序列和到目前為止的感知情況下,表征智能體對其所處物理狀態的信度。置信狀態通俗的說,就是智能體所有可能的物理的狀態的集合。
3.4.1無觀測的搜索
? ? 和常識相反,無觀測搜索大多數情況下是可解的,而且具有重要的實際意義。首先是不依賴傳感器,當傳感器發生故障的時候,這是相當重要的。第二是傳感器成本太高或者無法使用傳感器的情況。
? ? 為了解決無觀測問題,我們在置信狀態空間中搜索,而不是在物理狀態空間中搜索。需要注意的是,在置信狀態空間中,是完全可觀測的,因為智能體知道它處于哪一個置信狀態。而且結構也只是一個動作序列,因為沒有感知,所以沒有和前面使用與-非樹時一樣的通過輸入改變動作的部分了。
? ? 假定潛在的物理問題P可以通過以下定義:ACTIONp,RESULTp,GOAL-TESTp,STEP-COSTp。那么我們可以通過按照如下所述來定義相應的無感知問題:
對確定性動作:b' = RESULT(b,a) = {s':s'= RESULTp(s,a) and s∈b}
其中b'是轉移之后的置信狀態,b為轉移之前的置信狀態,a為轉移發生的動作。
生成新的置信空間的過程稱為預測:b' = PREDICTp(b,a)
對非確定性動作:
? ? 通過置信狀態來搜索無疑仍舊是一個笨辦法……稍稍的改進搜索是,首先假定一個狀態為初始狀態,然后搜索出一個可行方案,將方案應用到另一個狀態為初始狀態的環境下,如果方案能夠成立,則應用于第三個狀態為初始狀態的環境下,否則返回原點重新尋找一個方案,直至找到一個能夠滿足所有初始狀態的方案。
3.4.2有觀測的搜索
? ?在有觀測的搜索當中,ACTIONS、STEP-COST和GOAL-TEST同無觀測搜索一致,轉移模型Transition Model則稍顯復雜。我們將在特定動作下由一個置信狀態到下一個置信狀態的轉移劃分為三個階段。
b'' = UPDATE(b',o) = {s : o=PERCEPT(s) and?s∈b'}
? ?將這三步合起來就是:
3.5在線搜索智能體和未知環境
? ? 到目前為止,我們一直在使用離線搜索算法,它在涉足真實世界之前先計算出解決方案,之后執行。與之相反,在線搜索智能體計算與行動交錯進行。首先它采取一個行動,然后再觀測,通過觀測值再計算出下一步行動動作。在線搜索對未知環境問題至關重要,同時對常規問題也很重要,它可以使智能體的計算集中于已經發生的事情上,而不是可能發生但最終沒有發生的事情上。當環境未知時,智能體面臨探索問題。
? 3.5.1在線搜索問題
? ? 在線搜索問題是必須通過執行動作來完成的,而不能僅僅通過計算得到答案。我們假定一個確定并且完全可觀測的環境。但是智能體只能知道如下信息:
注意:智能體不能決定RESULT(s,a),除非它處于s狀態且執行了a動作,它才能知道結果。
? ? 最后,智能體肯那個擁有一個啟發函數h(s),用于評估從當前狀態到目標狀態的距離。我們把在線搜索的路徑耗散和如果已知環境尋找到路徑的耗散的比值稱為競爭比。當搜索路徑中有“死胡同”時,競爭比為無窮大。為簡單起見,我們假設搜索空間是安全搜索的,也就是說沒有“死胡同”。
? 3.5.2在線搜索智能體
function ONLINE-DFS-AGENT(s?) returns an action
inputs: s', a percept that identifies the current state
persistent: result , a table indexed by state and action, initially empty
untried, a table that lists, for each state, the actions not yet tried
unbacktracked, a table that lists, for each state, the backtracks not yet tried
s, a, the previous state and action, initially null
if GOAL-TEST(s'?) then return stop
if s'? is a new state (not in untried) then untried[s?]←ACTIONS(s?)
if s is not null then
result [s, a]←s?'
add s to the front of unbacktracked[s'?]
if untried[s?'] is empty then
if unbacktracked[s?] is empty then return stop
else a←an action b such that result [s?, b] = POP(unbacktracked[s?])
else a←POP(untried[s?])
s ←s?'
return a
? ? 以上為使用深度優先搜索策略的在線搜索算法。
? 3.5.3在線局部搜索
? ?深度優先策略能夠應用于在線搜索,是因為其展開節點策略的局部性,不必回溯去展開所有的狀態的后繼節點以便搜索。與此類似,爬山算法也有著良好的局部性。事實上,由于爬山算法保存了當前狀態值,它已經是一個在線搜索算法了。但最簡單的爬山算法并不適用于在線局部搜索。首先它會是智能體處于啟發函數值最大或最小的地方,但不會到新的地方去,這樣無法到達目的地。其次它隨機開始的特性不適用于在線搜索,因為智能體不能讓自己突然移動到任何位置。一下為一個改進后的使用爬山策略的在線局部搜索算法。
3.5.3學習
? ?智能體起初對環境一無所知,但隨著其對環境的探索,它會逐漸生成對環境的地圖,這就是學習。在采用啟發式的情況下,其記錄每個位置的耗散,也是一種學習。
4.總結
? 本章主要講解了以下內容。