人工智能中的搜索:
搜索算法的形式化描述:<状态、动作、状态转移、路径、测试目标>
状态:从原问题转化出的问题描述。
动作:从当前时刻所处状态转移到下一时刻所处状态。
状态转移:对某一时刻对应状态进行某一种操作后,所能到达的状态。
路径:一个状态序列。该状态序列呗一系列操作所连接。
目标测试:评估当前状态是否为所求解的目标状态。
2.1 启发式搜索(有信息搜索)
在搜索的过程中利用与所求解问题相关的辅助信息,其代表算法为贪婪最佳优先搜索和A*搜索。
辅助信息:所求解问题之外、与所求解问题相关的特定信息或知识。
评价函数:从当前节点出发,根据评价函数来选择后续节点。
启发函数:计算从节点n到目标节点之间所形成路径的最小代价值。
贪婪最佳优先搜索:
- 评价函数 = 启发函数
- 不足:贪婪最佳优先搜索不是最优的
- 启发式函数代价最小化这一目标会对错误的起点比较敏感。
A*算法:
- 定义评价函数: f(n) = g(n) + h(n)
- g(n)表示从起始节点到节点n的开销代价值,h(n)表示从节点n到目标节点路径中所估算的最小开销代价价值。
- f(n)可视为经过节点n、具有最小开销代价值的路径。
A*算法
启发式搜索:启发式搜索就是再状态空间中搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索知道目标。这样可以省略大量无谓的搜索路径,提高了效率。再启发式搜索中,对未知的估价是十分重要的。采用了不同的估价可以有不同的效果。
估价函数:从当前节点移动到目标节点的预估费用:这个估计就是启发式的。再寻路问题和迷宫问题中,我们通常采用曼哈顿估价函数预估费用。
A*算法与BFS:BFS是A*算法的一个特例,对于一个BFS算法,从当前节点扩展出来的每一个节点都要放进队列进行进一步扩展。也就是说BFS的估计函数h永远等于0,没有一点启发式的信息,可以认为BFS是最烂的A*算法。
选取最小估价:对于每次选取最小估价的节点,应该用到最小优先级队列。
A*算法的特点:A*算法再理论上是最优的,但是也有缺点:它的空间增长是指数级别的。
IDA*算法:这种算法被称为迭代加深A*算法,可以有效解决A*空间增长带来的问题。
搜索区域
假设有人想从A点移动到一墙之隔的B点,如下图,绿色的是起点A,红色是终点B,蓝色方块是中间的墙。
你首先注意到,搜索区域被我们划分成了方形网格。像这样,简化搜索区域,是寻路的第一步。这一方法把搜索区域简化成了一个二维数组。数组的每一个元素是网格的一个方块,方块被标记为可通过的和不可通过的。路径被描述为从A到B我们经过的方块的集合。一旦路径被找到,我们的人就从一个方格的中心走向另一个,直到到达目的地。
开始搜索
正如我们处理上图网格的方法,一旦搜索区域被转化为容易处理的节点,下一步就是去引导一次找到最短路径的搜索。在A*寻路算法中,我们通过从点A开始,检查相邻方格的方式,向外扩展直到找到目标。
我们做如下操作开始搜索:
- 从点A开始,并且把它作为待处理点存入一个“开启列表”。开启列表就像一张购物清单。尽管现在列表里只有一个元素,但以后就会多起来。你的路径可能会通过它包含的方格,也可能不会。基本上,这是一个待检查方格的列表。
- 寻找起点周围所有可到达或者可通过的方格,跳过有墙,水,或其他无法通过地形的方格。也把他们加入开启列表。为所有这些方格保存点A作为“父方格”。当我们想描述路径的时候,父方格的资料是十分重要的。后面会解释它的具体用途。
- 从开启列表中删除点A,把它加入到一个“关闭列表”,列表中保存所有不需要再次检查的方格。在这一点,你应该形成如图的结构。在图中,暗绿色方格是你起始方格的中心。它被用浅蓝色描边,以表示它被加入到关闭列表中了。所有的相邻格现在都在开启列表中,它们被用浅绿色描边。每个方格都有一个灰色指针反指他们的父方格,也就是开始的方格。
接着,我们选择开启列表中的临近方格,大致重复前面的过程,如下。但是,哪个方格是我们要选择的呢?是那个F值最低的。
路径评分
选择路径中经过哪个方格的关键是下面这个等式:F = G + H
这里:
- G = 从起点A,沿着产生的路径,移动到网格上指定方格的移动耗费。
- H = 从网格上那个方格移动到终点B的预估移动耗费。
这经常被称为启发式的,可能会让你有点迷惑。这样叫的原因是因为它只是个猜测。我们没办法事先知道路径的长度,因为路上可能存在各种障碍(墙,水,等等)。虽然本文只提供了一种计算H的方法,但是你可以在网上找到很多其他的方法。
我们的路径是通过反复遍历开启列表并且选择具有最低F值的方格来生成的。文章将对这个过程做更详细的描述。首先,我们更深入的看看如何计算这个方程。
正如上面所说,G表示沿路径从起点到当前点的移动耗费。在这个例子里,我们令水平或者垂直移动的耗费为10,对角线方向耗费为14。我们取这些值是因为沿对角线的距离是沿水平或垂直移动耗费的的根号2,或者约.414倍。为了简化,我们用和近似。比例基本正确,同时我们避免了求根运算和小数。这不是只因为我们怕麻烦或者不喜欢数学。使用这样的整数对计算机来说也更快捷。你不就就会发现,如果你不使用这些简化方法,寻路会变得很慢。
既然我们在计算沿特定路径通往某个方格的G值,求值的方法就是取它父节点的G值,然后依照它相对父节点是对角线方向或者直角方向(非对角线),分别增加和。例子中这个方法的需求会变得更多,因为我们从起点方格以外获取了不止一个方格。
H值可以用不同的方法估算。我们这里使用的方法被称为曼哈顿方法,它计算从当前格到目的格之间水平和垂直的方格的数量总和,忽略对角线方向,然后把结果乘以10。这被称为曼哈顿方法是因为它看起来像计算城市中从一个地方到另外一个地方的街区数,在那里你不能沿对角线方向穿过街区。很重要的一点,我们忽略了一切障碍物。这是对剩余距离的一个估算,而非实际值,这也是这一方法被称为启发式的原因。想知道更多?你可以在这里找到方程和额外的注解。
F的值是G和H的和。第一步搜索的结果可以在下面的图表中看到。F,G和H的评分被写在每个方格里。正如在紧挨起始格右侧的方格所表示的,F被打印在左上角,G在左下角,H则在右下角。
现在我们来看看这些方格。写字母的方格里,G = 10。这是因为它只在水平方向偏离起始格一个格距。紧邻起始格的上方,下方和左边的方格的G值都等于10。对角线方向的G值是14。
H值通过求解到红色目标格的曼哈顿距离得到,其中只在水平和垂直方向移动,并且忽略中间的墙。用这种方法,起点右侧紧邻的方格离红色方格有格距离,H值就是30。这块方格上方的方格有格距离(记住,只能在水平和垂直方向移动),H值是40。你大致应该知道如何计算其他方格的H值了~。每个格子的F值,还是简单的由G和H相加得到。
继续搜索
为了继续搜索,我们简单的从开启列表中选择F值最低的方格。然后,对选中的方格做如下处理:
4.把它从开启列表中删除,然后添加到关闭列表中。
5.检查所有相邻格子。跳过那些已经在关闭列表中的或者不可通过的(有墙,水的地形,或者其他无法通过的地形),把他们添加进开启列表,如果他们还不在里面的话。把选中的方格作为新的方格的父节点。
6.如果某个相邻格已经在开启列表里了,检查现在的这条路径是否更好。换句话说,检查如果我们用新的路径到达它的话,G值是否会更低一些。如果不是,那就什么都不做。
另一方面,如果新的G值更低,那就把相邻方格的父节点改为目前选中的方格(在上面的图表中,把箭头的方向改为指向这个方格)。最后,重新计算F和G的值。如果这看起来不够清晰,你可以看下面的图示。
好了,让我们看看它是怎么运作的。我们最初的格方格中,在起点被切换到关闭列表中后,还剩格留在开启列表中。这里面,F值最低的那个是起始格右侧紧邻的格子,它的F值是40。因此我们选择这一格作为下一个要处理的方格。在紧随的图中,它被用蓝色突出显示。
首先,我们把它从开启列表中取出,放入关闭列表(这就是他被蓝色突出显示的原因)。然后我们检查相邻的格子。哦,右侧的格子是墙,所以我们略过。左侧的格子是起始格。它在关闭列表里,所以我们也跳过它。
其他格已经在开启列表里了,于是我们检查G值来判定,如果通过这一格到达那里,路径是否更好。我们来看选中格子下面的方格。它的G值是。如果我们从当前格移动到那里,G值就会等于(到达当前格的G值是,移动到上面的格子将使得G值增加)。因为G值大于,所以这不是更好的路径。如果你看图,就能理解。与其通过先水平移动一格,再垂直移动一格,还不如直接沿对角线方向移动一格来得简单。
当我们对已经存在于开启列表中的个临近格重复这一过程的时候,我们发现没有一条路径可以通过使用当前格子得到改善,所以我们不做任何改变。既然我们已经检查过了所有邻近格,那么就可以移动到下一格了。
于是我们检索开启列表,现在里面只有7格了,我们仍然选择其中F值最低的。有趣的是,这次,有两个格子的数值都是54。我们如何选择?这并不麻烦。从速度上考虑,选择最后添加进列表的格子会更快捷。这种导致了寻路过程中,在靠近目标的时候,优先使用新找到的格子的偏好。但这无关紧要。(对相同数值的不同对待,导致不同版本的A*算法找到等长的不同路径)那我们就选择起始格右下方的格子,如图:
这次,当我们检查相邻格的时候,发现右侧是墙,于是略过。上面一格也被略过。我们也略过了墙下面的格子。为什么呢?因为你不能在不穿越墙角的情况下直接到达那个格子。你的确需要先往下走然后到达那一格,按部就班的走过那个拐角。(注解:穿越拐角的规则是可选的。它取决于你的节点是如何放置的。)(再次注解:我认为要根据实际情况以及题目描述分析。)
这样一来,就剩下了其他格。当前格下面的另外两个格子目前不在开启列表中,于是我们添加他们,并且把当前格指定为他们的父节点。其余格,两个已经在关闭列表中(起始格,和当前格上方的格子,在表格中蓝色高亮显示),于是我们略过它们。最后一格,在当前格的左侧,将被检查通过这条路径,G值是否更低。不必担心,我们已经准备好检查开启列表中的下一格了。
我们重复这个过程,直到目标格被添加进关闭列表(注解),就如在下面的图中所看到的。
注意,起始格下方格子(注解:是下下方的格子)的父节点已经和前面不同的。之前它的G值是88,并且指向右上方的格子。现在它的G值是80,指向它上方的格子。这在寻路过程中的某处发生,当应用新路径时,G值经过检查变得低了-于是父节点被重新指定,G和F值被重新计算。尽管这一变化在这个例子中并不重要,在很多场合,这种变化会导致寻路结果的巨大变化。
那么,我们怎么确定这条路径呢?很简单,从红色的目标格开始,按箭头的方向朝父节点移动。这最终会引导你回到起始格,这就是你的路径!看起来应该像图中那样。从起始格A移动到目标格B只是简单的从每个格子(节点)的中点沿路径移动到下一个,直到你到达目标点。就这么简单。
方法总结
1.把起始格添加到开启列表。
2.重复如下的工作:
a) 寻找开启列表中F值最低的格子。我们称它为当前格。
b) 把它切换到关闭列表。
c) 对相邻的格中的每一个?
* 如果它不可通过或者已经在关闭列表中,略过它。反之如下。
* 如果它不在开启列表中,把它添加进去。把当前格作为这一格的父节点。记录这一格的F,G,和H值。
* 如果它已经在开启列表中,用G值为参考检查新的路径是否更好。更低的G值意味着更好的路径。如果是这样,就把这一格的父节点改成当前格,并且重新计算这一格的G和F值。如果你保持你的开启列表按F值排序,改变之后你可能需要重新对开启列表排序。
d) 停止,当你
* 把目标格添加进了关闭列表(注解),这时候路径被找到,或者
* 没有找到目标格,开启列表已经空了。这时候,路径不存在。
3.保存路径。从目标格开始,沿着每一格的父节点移动直到回到起始格。这就是你的路径。
A*算法保持最优的条件:启发函数的可容性和一致性。
- 将直线距离作为启发函数h(n),则启发函数一定是可容的,因为其不会高估开销代价。
- g(n)是从其实节点到节点n的实际代价开销,且f(n) = g(n) + h(n) ,因此f(n) 不会高估经过节点n路径的实际开销。
- 树搜算法中,如果启发函数是可容的,则算法是最优的和完备的;再图搜算法中,如果启发函数是一致的,算法是最优的。
- 如果函数满足一致性条件,则一定满足可容条件,反之不然。
2.2 对抗搜索
对抗搜索也成为博弈搜索。
在一个竞争的环境中,智能体之间通过竞争实现相反的利益,一方最大化这个利益,另外一方最小化这个利益。
主要内容:
- 最小最大搜索:最小最大搜索时在对抗搜索中最为基本的一种让玩家来计算最优策略的方法。
- Alpha-Beta剪枝搜索:一种对最小最大搜索进行改进的算法,即在搜索过程中可剪除无需搜索的分支节点,且不影响搜索结果。
- 蒙特卡洛树搜索:通过采样而非穷举法实现搜索。
2.3 蒙特卡洛树搜索
2.3.1 单一状态蒙特卡洛规划
多臂赌博机:
- 单一状态,k种行动;
- 在摇臂赌博机问题中,每次以随机采样形式采取一种行动a,好比随机拉动第k个赌博机的臂膀,得到R(s, ak)的回报。
多臂赌博机问题是一种序列决策问题,这种问题需要在利用和探索之间保持平衡。
利用:保证在过去决策中得到最佳回报。
探索:寄希望在未来能够得到更大回报。
2.3.2 上限置信区间
UCB算法既需要拉动在过去时间内获得最大平均奖赏的赌博机,又希望去选择拉动臂膀次数最少的赌博机。
2.3.3 蒙特卡洛树搜索
将上限置信区间算法应用于游戏树的搜索方法,包括四个步骤:选举、扩展、模拟、反向传播。
选择:
- 从根节点R开始,向下递归选择子节点,直至选择一个叶子节点L。
- 用UCB选择最具潜力的后续节点。
扩展:
- 如果L不是一个终止节点,则随机创建其后的一个未被访问的节点,选择该节点作为后续子节点C。
模拟:
- 从节点C出发,对游戏进行模拟,直至博弈游戏结束。
反向传播:
- 用模拟所得结果来回溯更新导致这个结果的每个节点中获胜次数和访问次数。
两种策略学习机制:
- 搜索树策略:从已有的搜索树中选择或创建一个叶子节点(选择和扩展),搜索树在利用和探索之间保持平衡。
- 模拟策略:从非叶子节点出发模拟游戏,得到游戏仿真结果。
蒙特卡洛树搜索基于采样来得到结果、而非穷举式枚举。