人原相揖别
Search
Trivial Search
搜索问题:
- 有 状态空间 (state space)。
- 有 后继函数 (successor function),给出下一步的动作和代价。
- 起始态和终止态。
- 解法 (solution) 是一系列从起始态到终止态的动作。
状态空间图 (state space graph) 是状态空间与后继函数共同确定的有向图。
搜索树 (search tree) 是从起始态开始,每一步操作及其对应结果构成的树。
DFS。BFS。迭代加深 (Iterative Deepening)。一致代价搜索 (Uniform Cost Search, UCS) 是没见过的概念 是 Dijkstra,会访问所有代价不超过最优解法的态。
Heuristic Search
来点启发式搜索。一个 启发 (heuristic) \(h(x)\) 估算当前态有多么接近目标,对每个搜索问题具有特异性。另一方面,记 \(g(x)\) 为到 \(x\) 时经过的代价,即 Uniform Cost。A* 使用 \(h(x)+g(x)\) 作为顺序进行搜索。
可接受启发 (admissible heuristics):不超过真实代价的启发,即满足 \(0\leq h(A)\leq\t{actual cost}\)。此时 A* 是正确的。
对于最优的 \(A\) 和次优的 \(B\),有 \(g(A)<g(B)\) 但 \(h(A)=h(B)=0\),因此 \(A\) 必然先于 \(B\) 出队。进一步,对于 \(A\) 的所有祖先 \(C\),有 \(g(A)+h(C)\leq h(A)\),所以它们都先于 \(B\) 出队。
但是这同时也意味着 A* 必须等某个目标状态出队时才能中止搜索。
启发之间可以定义偏序关系,即在所有位置上,一者都不小于另一者。此时所有可接受启发构成一个 semi-lattice:其底部是全 \(0\) 启发,顶部是真实剩余距离。
Graph Search:记录所有曾访问过的 state,避免重复访问。但是,正如同 Dijkstra 一样,如果发现增广到新点时距离减少了,那么还是要再扔进去增广的,效果并没有变好很多。
但是,如果使用的启发是 一致性启发 (consistent heuristics),即弧长不超过真实代价的启发,满足 \(h(A)-h(C)\leq\t{cost}(A\to C)\)。此时有
即任何路径上的 A* 顺序不降。这就导致任何状态都不会在转移至其的最优态被展开前展开,换句话说,任何状态被第一次展开时,就已经是最优态了。那么所有点就都只会被增广一次。
CSP
搜索可以处理 规划 (planning) 即操作序列,或者 识别 (identification) 即为变量分配值,等类型的问题。
Constraint Satisfaction Problems (CSPs):状态是一组取值来自 \(D\) 的变量 \(X_i\)(有时 \(D\) 与下标 \(i\) 亦相关),目标态是找到一种变量取值方案,满足所有限制,其中每个限制是针对某个子集的取值。例如图染色问题。
Binary CSP:所有限制都是二元的。此时可以绘制 Binary Constraint Graph。图染色、\(n\) 皇后、数独等,都可以被归结为二元 CSP。
Discrete CSP:变量的值域是离散的。有限域的场合,如 2SAT 等。可数域的场合,域是整数或字符串,此时如分配不交工作等。
Continuous CSP:变量的值域是连续的。
约束可以是禁止型的,此时可以有一元约束(禁止某处为某值)、二元约束(禁止两处相等)、多元约束等。当然也可以是软约束(偏好),认为一者好于另一者,此时偏好可以用代价描述,问题由可行性问题变为最优性问题。
回溯搜索 (backtracking search) 是常见的用来求解 CSP 的方法,即:按照固定顺序依次确定每个变量的值;一旦发现出现矛盾就回溯。可以使用某些方法优化:
- filtering:在早期识别不可避免的失败。引入 弧相容性 (arc consistency) 的概念:称一个弧 \(x\to y\) 是相容 (consist) 的,如果对于 \(x\) 的所有取值,\(y\) 都有至少一个合法取值。如果一个弧不相容,那么我们可以自 \(x\) 的值域中移除某些必然不合法的取值。因此有 AC-3 算法:首先将所有弧入队;然后每次出队并检测相容性,如果不相容,就移除 \(x\) 中不合法的东西,并且将所有指向 \(x\) 的弧重新入队。当然,也可以有有元数更高的相容性(对于所有前 \(n-1\) 个的相容方案,都能被延申到第 \(n\) 个)——但是代价更高。如果一个问题有 强 \(K\)-相容性 (strong \(K\)-consistency),即所有子集都相容,那么直接随便填,不需要回溯就必然有解。
- ordering:每次都先增广那些剩余合法取值最少的位置(Minimum Remaining Values, MRV 法则),这是在希望找到那些失败最快的路径,以优先排除无效分支,并锚定那些必选项。在此之上,先尝试那些对邻居变量取值限制最小的方案(Least Constraining Value, LCV 法则)——它们更可能是解。
- structure:识别问题内在结构(如树形结构)或子问题。
除了大力搜外,还有经典的随机调整法(Iterative Algorithm):对于一个完整但有矛盾的赋值方案,每次随机选一个矛盾的位置,然后将其赋为产生矛盾最小的新值,重复直到无矛盾。这就是 Local Search 也就是「爬山法」的思想——它更快、更省空间,但显然并非完备。
爬山都来了,那退火也就来了。此外还有遗传算法:首先仅保留在某个标准下 top-\(K\) 方案,然后随机在各个方案中交换、变异等。但好像不牛。
Adversarial Search
对抗博弈 (Adversarial Game):
- 有状态集合 \(S\) 和起始状态 \(s_0\)。
- 有两个玩家 \(\t{MAX}\) 和 \(\t{MIN}\)。
- 有操作集合 \(A\)(可能与玩家和状态相关)。
- 有转移函数 \(S\times A\to S\)。
- 有终止态集合,和终止态效用 \(S\to R\)。\(\t{MAX}\) 要最大化效用,\(\t{MIN}\) 要最小化。
一个玩家的解法是 \(S\to A\) 的策略。
所以有 minimax 搜索 (minimax search):要计算所有态的 minimax 值 \(V\),所有终态的 \(V\) 都是确定的;MAX 方的 \(V\) 是所有后继态 \(V\) 的最大值;MIN 方的 \(V\) 是所有后继态 \(V\) 的最小值。这玩意要搜完整棵决策树,效率很低。
Alpha-Beta Pruning
因此可以剪枝——有 alpha-beta 剪枝 (alpha-beta pruning)。假设当前在求某个 MIN;在考虑完一个前缀的子状态时,我们可以确保最终的 MIN 不超过当前的 MIN;但是这个 MIN 态的父亲是一个 MAX 态,它同样考虑完一个前缀的子状态,有一个当前的 MAX;如果 MIN ≤ MAX,则这整棵子树对于父亲的 MAX 不可能产生贡献,因此可以直接被剪枝。这一推理适用于至根路径上所有的 MAX:只要当前 MIN ≤ 路径上任何一个 MAX,它的贡献就无法传播到其之上。
于是可以有如下的 MIN 端代码:
- 输入:状态 \(s\),阈值 \(\alpha,\beta\)。
- 初始化结果 \(v=\infty\)。
- 枚举所有后继态 \(s'\):
- 计算 \(v\gets\min\Big(x,\t{value}(s',\alpha,\beta)\Big)\)。
- 如果 \(v\leq\alpha\),直接掐断。
- \(\beta\gets\min(\beta,v)\)。
- 返回 \(v\)。
MAX 端代码同理。
在儿子的顺序最优的情况下,它的复杂度 \(O(b^d)\to O(b^{d/2})\),相当于能搜索的深度翻倍。
但就算这样,搜到底还是不可接受的。所以只能设置一个深度阈值(depth-limited minimax),然后使用某种估算策略,对到达阈值的态使用一个 评估函数 (evaluation function) 来预测 minimax 值。当然,这么做就不再保证最优了。
常见的评估函数是一堆特征提取函数 \(f_1(s),\dots,f_n(s)\) 的带权 \(w_1,\dots,w_n\) 和。这些特征提取函数可以依靠人工构建,也可以使用各种神经网络方法。
在越深的地方使用评估函数来代替搜索,其精度需求越低——这需要权衡。当然也可以使用迭代加深。
同时,评估函数也可以被用于优化 alpha-beta 中访问后继的顺序,优先访问最有前景的点,以在后面的剪枝中派上更大用场。同理,它也可以被用于提前剪枝:还是以 MIN 时为例,评估函数可以在进入该状态时就给一个预测,假如这个预测 \(\leq\alpha\),则也可以被剪掉了。MAX 同理。
这一分析还能被延申到多玩家(例如,两个 MIN 和一个 MAX)的场合。此时,因为所有人的策略公开,且两个 MIN 的目标相同,它们会自然地合作。
Expectimax Search
Minimax 看起来不错,但是它一方面太过悲观,总是假设对手是拥有全部信息的理性人;一方面也没有考虑到决策过程中的随机性(如扔骰子)。
所以就有 Expectimax Search 方法,其除了 max node、min node 外,还引入了 chance node,衡量概率事件。这些点的 \(V\) 是后继态的期望。
chance 节点处不能应用 alpha-beta 剪枝——坏的后继可能因为概率低而无足轻重。
Monte Carlo Tree Search
如果可能的决策数量过大,minimax 就用不了了。此时 Monte Carlo 树搜索 (Monte Carlo Tree Search, MCTS) 是一个可行的方法。其效果是,通过若干轮模拟操作,逐渐寻找到最优方案。
其主要思想是维护一棵不断扩张的模拟博弈树,根节点为待决策状态 \(s_0\),向下的每一步是即为博弈中的一次行动,且该决策已经在先前的模拟中被验证过(当然,因为是博弈,每一层对应的决策者都是交替的——如果上一层在模拟黑方行为,那么下一层就是在模拟白方行为)。
所有节点上维护两个信息:\(n\),表示该节点子树中所有验证数目(同时也是该节点被访问的次数——每次被访问,都意味着子树中某个节点处展开了验证);\(q\),该节点子树中展开过的所有模拟的总奖励。为了保证统计公平,同一个节点的所有儿子应该从同一个角度记录收益——即它们共同父节点的行动方。\(q/n\) 越大,就意味着从父节点到子节点的这步行动对决策方越优。
其面对当前的状态 \(s_0\),重复若干轮下述操作,:
- 选择 (Selection) 阶段,找到一种「富有潜力」的路径。
- 从根节点开始,不断向下递归,直到找到一个节点,其存在未模拟过的子状态(即,有一步决策——不管是己方的还是对方的——没有被研究过)。递归的方式存在某种偏好,将在下文提到。
- 扩展 (Expansion) 阶段,创建一个子节点。
- 当前的节点存在某个未模拟过的子状态,那么我们当然要看看它怎么样。直接在所有未模拟过的子状态中随机挑一个(即当前行动方进行一步决策),加入博弈树。
- 模拟 (Simulation) 阶段,评估这个子状态效果如何。
- 非常简单粗暴,直接双方 随机决策 直到对局结束,目标是为了速度而非质量(称为 light playout)。也可以引入一些轻量级启发式操作(称为 heavy playout)。游戏结束后得到输/赢/平局的反馈。
- 反向传播 (Backpropagation) 阶段,将子状态的反馈回报给整条决策路径。
- 从建立的新节点开始,将路径上所有节点的 \(n\) 增加一,并且将与被模拟节点的胜者方相同的节点的 \(q\) 增加一,(视具体实现细节而定)将与败者方相同的节点的 \(q\) 减少一。
假如忽略对「选择」阶段对子节点的偏好,这其实就是一个评估函数比较魔怔(通过纯随机快速评估)的类似 depth-limit minimax 的方法。当然,因为引入了 Monte-Carlo,多次实验的平均结果或许能较好地实现评估效果。
当然,MCTS 的核心就在于它设计的 Upper Confidence Bound (UCB) 函数,来平衡在选择新节点时,「更有潜力」和「尚未探索」这两个目标。
具体而言,有
其中,前一项是 利用项,即该节点的胜率——胜率越高,越值得被利用;而后一项是 探索项,在其父亲的所有儿子中,访问次数越小的儿子该项越大,奖励了对访问次数较少节点的探索。\(c\) 是可调节参数。
在选择阶段,每一次都选择所有儿子中 UCB 最高的那个进行探索。
在所有模拟均执行完后,结束。真实决策采用根节点所有儿子中,访问次数 \(n\) 最多的一个——因为由 UCB 公式,最有前景的决策,就是访问次数最多的。
Bayesian Net
Topology
假设有一个联合分布,则通过 Bayesian 公式可以简单求条件概率。
但是联合分布会是一个高维表,不好存储。于是我们作出一些假设,比如随机变量间具有某些独立性。
建立一张 DAG,每个节点都对应一个随机变量,且存储着所有入边到其的条件概率表:一旦其所有入边的取值均给定,则该随机变量的分布也随之确定。这样,我们就将一个大表拆成了很多小表,复杂度随之下降。
这个假设可以导出性质:假如一个节点的所有父节点均给定,则节点与其所有非后代均独立;这一性质与条件概率表无关。
事实上,这个性质还能进一步被扩张。仅靠 DAG 的拓扑结构即可确定一些独立性:称一组变量 \(Z_1,\dots,Z_k\) D-Separate 随机变量 \(X,Y\),如果不论条件概率表是什么,总有给定 \(Z_1,\dots,Z_k\) 时 \(X,Y\) 独立。
- 因果关系 \(X\to Z\to Y\)。此时如果 \(Z\) 给定则 \(X,Y\) 无法经由此关系关联,否则它们间存在关联。
- 共因关系 \(Y\gets Z\to X\)。此时同理,如果 \(Z\) 给定则 \(X,Y\) 无法关联,否则可以关联。
- 共果关系 \(Y\to Z\gets X\)。此时如果 \(Z\) 未给定则 \(X,Y\) 无法关联,否则反倒可以关联了。进一步,只要 \(Z\) 的任何后代被给定了则都可以关联。
因此可以得到如下的算法:
- 求出所有 \(Z\) 及其祖先集合 \(A\)。
- 建新图:新图是原图的无向复制,但移除了所有 \(Z\) 中节点,同时对于每个 \(A\) 中节点,将其所有入边两两连边(无向边)。
- 判可达性。
这个算法完美求出了所有 D-分割。
不仅如此,我们有如下 可表示性定理:给定 Bayesian Net 的拓扑结构,则所有其可表示的联合分布恰为满足全体 D-分割的分布。换言之,拓扑结构提供了一个 Hypothesis Class。
Bayesian Net 中的边不一定反映因果关系,而仅仅反映相关性(例如,同一个因的两个果常常具有很高的相关性)。但如果依照因果关系构建,效果通常更好:因为这样的图通常更稀疏、容易理解,且容易从人类专家处学习。归根到底,Bayesian 网络的灵魂就是 D-分割。
Inference
如果给定一堆 r.v. 的观察(即确定一批 \(E_1,\dots,E_k=e_1,\dots,e_k\)),如何查询在此条件下某变量 \(Q\) 的分布?
换言之,我们可以枚举全体 \(h\) 求和然后归一化。
暴力的做法就不提了,来点更精巧的方法。
我们先忽略 Bayesian Net 的有向性,仅仅将其看做是一堆函数表 \(\cur{f_1(x,p)=P(X_1=x\mid\t{PRE}(X_1)=p),\dots,f_n(\cdot)=P(\cdot)}\)。则有
初始记录全体表(有一些维度会低于初始维度,因为 \(E\) 已经给定了),然后每次挑选一个 \(H\),枚举其取值后,将所有涉及到其的表关于该取值求和后合并为一个大表,不断合并下去,最终会得到一个仅涉及 \(Q\) 的表,就是终表。
事实上,上述算法本身不依赖于 Bayesian Net,而是对于一切函数表(即,将联合分布的大表拆成一堆牵涉变量有限的小表)均有效。但是 Bayesian Net 可以指导我们如何选择 \(H\) 更高效(其本质上是选择最小化树宽的遍历顺序)。
既然都涉及到树宽了,那么只需要造一个拥有高树宽的图,即可知该算法仍然是指数级别的。事实上,该问题本身是 NP-Hard 的。
到目前位置我们仍然没有体现出 Bayesian Net 的真正威力。事实上,其价值在于高效采样:只需按照拓扑序自条件概率小表中依次采样,即可从联合分布中采样。于是有以下近似算法:
- Rejection Sampling:不断采样,筛选出那些满足 \(E=e\) 的 sample,用频率近似概率。劣势是绝大部分样本都没用了。
- Likelihood Weighting:作 importance sampling,即确定 \(E\) 中变量的取值时,不采样而是给最终权值乘以该取值的概率。
- Gibbs Sampling:每次选择一维固定其它维,然后关于其它维当前取值采样,重复若干轮或直到收敛。事实上,「关于其它维」仅包括选择维度的 Markov Blanket,即所有与该节点可能非独立的点,包括:所有父节点;所有子节点;所有子节点的其它父节点。
Training
BN 的训练涉及两部分:对条件概率的训练和对拓扑结构的训练。
首先考虑对条件概率的训练。
假如不假设隐变量,则训练很简单,直接使用频率替代概率即可。考虑到训练数据中会有一些未出现的取值,为了避免除零所以可以设置先验分布,表现在式子上就是给所有频率上加了一个初值。
但假如有隐变量,则令条件概率是参数 \(\theta\)、数据是 \(X\),则希望最大化 \(\log P(X\mid\theta)=\log\sum_ZP(X,Z\mid\theta)\)。
那么此时使用 ELBO 推导,假设有 \(Z\) 的分布 \(q\),则有
现在我们希望进行一个 \(q,\theta\) 的联合最大化。做 Coordinate Ascent,则步骤如下:
- Expectation-Step:在 \(\theta\) 固定的前提下调整 \(q\)。在 \(\t{KL}=0\) 时取得,此时有 \(q=P(\cdot\mid X,\theta)\),也即计算当前设置下隐变量的后验概率(条件期望)。
- Maximization-Step:在 \(q\) 固定的前提下调整 \(\theta\),最大化 ELBO。可以求数值解或梯度下降。
(特别地,在 VAE 中,E-step 会限制 \(q\) 是 Gaussian,然后同时梯度传播更新 \(q\) 和 \(\theta\))。
假如没有 DAG 则也要学其结构。当然为了结构简单,可以惩罚过于复杂的结构然后爬山,或者利用互信息等手动搭图,或者二者结合等。

浙公网安备 33010602011771号