一、值迭代过程
上面是贝尔曼最优公式,之前我们说过,f(v)=v,贝尔曼公式是满足contraction mapping theorem的,能够求解除它最优的策略和最优的state value,我们需要通过一个最优v*,这个v*来计算状态pi*,而vk通过迭代,就可以求出唯一的这个v*,而这个算法就叫做值迭代。V(s)是状态s的最优价值,R是在状态s时执行动作a可获得的,y是折扣因子(衰减系数),还有状态概率矩阵P
1.1 初始化状态价值函数
我们说过,这个函数有两个未知量。v与pi,因此要计算最优策略,我们就需要先假设一个初始值。选择一个初始值先来表示每个状态的价值。假设我们就可以设置所有价值V(s)都为0
1.2 迭代更新价值函数
使用贝尔曼最优方程更新状态价值函数,对于与每个状态s,计算改状态下所有可能的动作a下的期望值,然后选择最大值作为新的状态价值函数。Vk是第k次迭代时s的状态,他会更新为k+1,直到k+1是最优时刻为止,具体的更新公式为:
这上面就包含了所说了两个步骤
第一步 ploicy update:
第二部 value update:
每次更新一个pik+1之后代入,就可以得到迭代后的vk+1,但是这里有个点,迭代过程中,左侧他是vk+1,所以他并不是我们所说的state value,他是一个值,
1.2.1 Ploicy update
我们把上面的公式具体的拆成每个状态对应的element,得到
vk是已知的(假设了v0,假设现在就是v0,求pi1),那么qk(s,a) (q1)是已知的,最优策略,就会选取qk最大时的action,其他行动为0,这样就只与q(s,a)相关,那么pik+1就知道了,就是pik+1(s)最大的一个
1.2.2 Value update
对于其elementwise form
按照迭代顺序写出每一个值,从1.2.1,我们就可以知道,qk(s,a)是能求出的,注意一点,策略迭代里面,求出了最大的value对应的state,那么我们就知道这个pik+1,求出最后的结果
1.3 判断收敛性
每次迭代后,检查状态价值函数的变化。如果状态价值变化小于某个阈值(例如 ϵ\epsilonϵ),则认为收敛,可以终止迭代。常见的收敛条件是:
通常 是一个小的正数,用于表示精度要求。如果状态价值函数的变化足够小,算法收敛。
根据例子,给出一个python代码
import numpy as np# 初始化参数
gamma = 0.9 # 折扣因子
epsilon = 1e-6 # 收敛阈值
max_iterations = 1000 # 最大迭代次数
S = 4 # 状态空间大小
A = 5 # 动作空间大小# 转移概率矩阵 P(s'|s, a) - 4x5x4 的三维矩阵
P = np.zeros((S, A, S))## 顺时针行动
# 奖励函数 R(s, a) - 4x5 的矩阵
R = np.array([[-1, 4, -1, -1, -1],[-1, 4, -1, -1, -1],[4, -1, -1, -1, -1],[-1, -1, -1, -1, 1]])# 转移概率矩阵
# 动作 a=1
P[:, 0, :] = np.array([[0.8, 0.1, 0.1, 0],[0.1, 0.8, 0.1, 0],[0.2, 0.2, 0.6, 0],[0, 0, 0, 1]])# 动作 a=2
P[:, 1, :] = np.array([[0.6, 0.3, 0.1, 0],[0.1, 0.7, 0.2, 0],[0.3, 0.3, 0.4, 0],[0, 0, 0, 1]])# 动作 a=3
P[:, 2, :] = np.array([[0.7, 0.2, 0.1, 0],[0.1, 0.8, 0.1, 0],[0.2, 0.2, 0.6, 0],[0, 0, 0, 1]])# 动作 a=4
P[:, 3, :] = np.array([[0.5, 0.4, 0.1, 0],[0.2, 0.7, 0.1, 0],[0.4, 0.4, 0.2, 0],[0, 0, 0, 1]])# 动作 a=5
P[:, 4, :] = np.array([[0.9, 0.05, 0.05, 0],[0.05, 0.9, 0.05, 0],[0.1, 0.1, 0.8, 0],[0, 0, 0, 1]])# 初始化状态价值函数 V(s)
V = np.zeros(S)# 记录最优策略
pi = np.zeros(S, dtype=int)# 值迭代算法
for k in range(max_iterations):V_new = np.zeros(S)delta = 0 # 最大值变化# 遍历每个状态for s in range(S):# 对每个动作计算期望回报value = -float('inf') # 当前最大回报(初始化为负无穷)for a in range(A):# 计算该动作下的期望回报expected_return = R[s, a] + gamma * np.sum(P[s, a, :] * V)value = max(value, expected_return) # 保持最大的期望回报# 更新当前状态的价值V_new[s] = valuedelta = max(delta, abs(V_new[s] - V[s])) # 计算状态价值的变化# 更新状态价值V = V_new# 如果变化小于 epsilon,认为收敛if delta < epsilon:break# 根据最优状态价值函数计算最优策略
for s in range(S):max_value = -float('inf')best_action = -1for a in range(A):# 计算每个动作下的期望回报expected_return = R[s, a] + gamma * np.sum(P[s, a, :] * V)if expected_return > max_value:max_value = expected_returnbest_action = api[s] = best_action# 输出结果
print("最优状态价值函数 V*(s):")
print(V)print("最优策略 pi*(s):")
print(pi)
MATLAB实现:
% 初始化参数
gamma = 0.9; % 折扣因子
epsilon = 1e-6; % 收敛阈值
max_iterations = 1000; % 最大迭代次数
S = 4; % 状态空间大小
A = 5; % 动作空间大小% 转移概率矩阵 P(s'|s, a) - 4x5x4 的三维矩阵
P = zeros(S, A, S);% 奖励函数 R(s, a) - 4x5 的矩阵
R = [-1, 4, -1, -1, -1;-1, 4, -1, -1, -1;4, -1, -1, -1, -1;-1, -1, -1, -1, 1];% 转移概率矩阵
% 动作 a=1
P(:, 1, :) = [0.8, 0.1, 0.1, 0; 0.1, 0.8, 0.1, 0; 0.2, 0.2, 0.6, 0; 0, 0, 0, 1];% 动作 a=2
P(:, 2, :) = [0.6, 0.3, 0.1, 0;0.1, 0.7, 0.2, 0;0.3, 0.3, 0.4, 0;0, 0, 0, 1];% 动作 a=3
P(:, 3, :) = [0.7, 0.2, 0.1, 0;0.1, 0.8, 0.1, 0;0.2, 0.2, 0.6, 0;0, 0, 0, 1];% 动作 a=4
P(:, 4, :) = [0.5, 0.4, 0.1, 0;0.2, 0.7, 0.1, 0;0.4, 0.4, 0.2, 0;0, 0, 0, 1];% 动作 a=5
P(:, 5, :) = [0.9, 0.05, 0.05, 0;0.05, 0.9, 0.05, 0;0.1, 0.1, 0.8, 0;0, 0, 0, 1];% 初始化状态价值函数 V(s)
V = zeros(S, 1);% 记录最优策略
pi = zeros(S, 1);% 值迭代算法
for k = 1:max_iterationsV_new = zeros(S, 1);delta = 0; % 最大值变化% 遍历每个状态for s = 1:S% 对每个动作计算期望回报value = -Inf; % 当前最大回报(初始化为负无穷)for a = 1:A% 计算该动作下的期望回报expected_return = R(s, a) + gamma * sum(squeeze(P(s, a, :)) .* V);value = max(value, expected_return); % 保持最大的期望回报end% 更新当前状态的价值V_new(s) = value;delta = max(delta, abs(V_new(s) - V(s))); % 计算状态价值的变化end% 更新状态价值V = V_new;% 如果变化小于 epsilon,认为收敛if delta < epsilonbreak;end
end% 根据最优状态价值函数计算最优策略
for s = 1:Smax_value = -Inf;best_action = -1;for a = 1:A% 计算每个动作下的期望回报expected_return = R(s, a) + gamma * sum(squeeze(P(s, a, :)) .* V');if expected_return > max_valuemax_value = expected_return;best_action = a;endendpi(s) = best_action;
end% 输出结果
disp('最优状态价值函数 V*(s):');
disp(V);disp('最优策略 pi*(s):');
disp(pi);
在这描述一下如何代码的运行流程,首先我们定义了衰减率,收敛阈值,需要迭代的次数,有多少个状态,有多少个动作,然后设置在每个状态下,动作的奖励R,还有状态概率矩阵P,在s状态下执行a跳到si的概率是多少,以代码和视频内的 4状态5动作举例,设置的P是个4*5*4的矩阵,策略pi会是个4*1的矩阵,他的作用就是记录4个状态的行动,所有有多少状态,他就是n*1.在正式值迭代里进行循环,首先需要给 V state value一个初始值,接着遍历每一个状态,在各个状态下采取行动,从s1到s4,计算a1到a5行动,代入到公式里
每一次计算就是一次标量的计算,value保存其中最大的值,然后对V进行更新,当更新值小于阈值的时候,就收敛停止。
现在计算出来每个状态的V,就对状态s遍历,用同样的方法进行遍历,但是这次需要记录获得最大值的a,把他记录到策略里。修改奖励与衰减系数可得到不同V
二、策略迭代过程
策略迭代 Policy lteration用于求解马尔可夫MDP最优策略的经典算法,其中有策略评估PE和策略改进PI两个关键步骤。策略迭代算法的核心思想是通过交替进行策略评估和策略改进两个步骤,不断优化策略,直到找到最优策略。具体来说,算法从一个初始策略开始,首先对该策略进行评估,计算出该策略下每个状态的价值函数;然后根据计算得到的价值函数对策略进行改进,得到一个新的策略;接着对新的策略进行评估,如此反复,直到策略不再发生变化,此时得到的策略即为最优策略 。算出整体的return进行比较,确认最好的策略,相当于从整体上看
2.1 初始化策略
值迭代我们说,需要一个初始的V,当然这里也是需要V的,就假设初始的state 策略迭代时生成了一个策略pi0(s),其中pi0(s)表示在状态s下采取动作的概率分布。
2.2 策略评估
给定一个策略 ,策略评估的目标是计算该策略下的状态价值函数 V 。状态价值函数V 表示从状态 s 开始,遵循策略 pi 所获得的期望累积折扣奖励。可以通过以下贝尔曼方程来计算:
按照迭代的方式我们写作
实际实现中,我们通常用的都是上面所说的之迭代来让state value收敛,这就不多做说明了
2.3 策略改进(PI)
根据评估得到了Vpi(s),评估了当前策略的好坏,但是我们知道,有了V就可以对pi进行改进,选择能让未来期望积累折扣奖励最大的动作作为新的策略,当策略不变化时,作为最优策略返回
值迭代通过直接优化state value,每次计算最大的期望回报,更新状态,最后根据最优状态函数来推导出最优策略.策略迭代就是通过上面所说的方法进行不断更新
% 初始化参数
gamma = 0.9; % 折扣因子
epsilon = 1e-6; % 收敛阈值
max_iterations = 1000; % 最大迭代次数
S = 4; % 状态空间大小
A = 5; % 动作空间大小% 转移概率矩阵 P(s'|s, a) - 4x5x4 的三维矩阵
P = zeros(S, A, S);% 奖励函数 R(s, a) - 4x5 的矩阵
R = [-1, 4, -1, -1, -1;-1, 4, -1, -1, -1;4, -1, -1, -1, -1;-1, -1, -1, -1, 1];% 转移概率矩阵
% 动作 a=1
P(:, 1, :) = [0.8, 0.1, 0.1, 0; 0.1, 0.8, 0.1, 0; 0.2, 0.2, 0.6, 0; 0, 0, 0, 1];% 动作 a=2
P(:, 2, :) = [0.6, 0.3, 0.1, 0;0.1, 0.7, 0.2, 0;0.3, 0.3, 0.4, 0;0, 0, 0, 1];% 动作 a=3
P(:, 3, :) = [0.7, 0.2, 0.1, 0;0.1, 0.8, 0.1, 0;0.2, 0.2, 0.6, 0;0, 0, 0, 1];% 动作 a=4
P(:, 4, :) = [0.5, 0.4, 0.1, 0;0.2, 0.7, 0.1, 0;0.4, 0.4, 0.2, 0;0, 0, 0, 1];% 动作 a=5
P(:, 5, :) = [0.9, 0.05, 0.05, 0;0.05, 0.9, 0.05, 0;0.1, 0.1, 0.8, 0;0, 0, 0, 1];% 初始化策略(均匀随机策略)
policy = ones(S, A) / A; % 每个状态下所有动作的概率均等% 初始化状态价值函数 V(s)
V = zeros(S, 1);% 策略迭代算法
for k = 1:max_iterations% 策略评估while trueV_new = zeros(S, 1);delta = 0; % 最大值变化% 遍历每个状态for s = 1:S% 计算当前策略下的期望回报expected_return = 0;for a = 1:Aexpected_return = expected_return + policy(s, a) * (R(s, a) + gamma * sum(squeeze(P(s, a, :)) .* V));endV_new(s) = expected_return;delta = max(delta, abs(V_new(s) - V(s))); % 计算状态价值的变化end% 更新状态价值V = V_new;% 如果变化小于 epsilon,认为收敛if delta < epsilonbreak;endend% 策略改进policy_stable = true; % 假设策略已经稳定for s = 1:S% 找到当前策略下的动作old_action = find(policy(s, :) == max(policy(s, :)), 1);% 计算每个动作的期望回报action_values = zeros(A, 1);for a = 1:Aaction_values(a) = R(s, a) + gamma * sum(squeeze(P(s, a, :)) .* V);end% 找到最优动作[~, best_action] = max(action_values);% 更新策略policy(s, :) = 0; % 清零policy(s, best_action) = 1; % 贪心策略% 检查策略是否变化if old_action ~= best_actionpolicy_stable = false;endend% 如果策略稳定,退出循环if policy_stablebreak;end
end% 输出结果
disp('最优状态价值函数 V*(s):');
disp(V);disp('最优策略 pi*(s):');
[~, pi] = max(policy, [], 2); % 从策略矩阵中提取最优动作
disp(pi);
同样的也来描述一样代码流程,策略评估的过程实际就是V计算的过程,这部分和值迭代是一样的,在值迭代的第二部,我们依然是遍历每一个状态下的a,找寻其最大值(其实是可以直接放到一起的)。在策略迭代执行的就是PI这一步,首先假设策略已经是稳定的了,然后遍历每个状态,再遍历每个动作,因为已经算出来vpik了,所以就可以计算qpik,新的动作就是选择最大qpik里的a。更新策略,当策略不再更新的时候,就是最优的。
三、截断策略迭代
Truncated Policy Iteration在传统的策略迭代中,策略评估步骤需要迭代求解贝尔曼方程,直到价值函数收敛,这在状态空间较大时明显就非常耗时。截断策略迭代则对策略评估步骤进行了截断,即只进行有限次的迭代更新,而不是一直迭代到收敛。这样虽然得到的价值函数可能不是精确的,但可以在较少的计算量下快速得到一个近似的价值函数,进而进行策略改进,加快整个算法的收敛速度。
3.1 初始化
初始化一个任意的策略 pi ,其中 pi0(s) 表示在状态 s 下采取动作的概率分布,同时也需要初始化V0 state value
3.2 截断策略评估
给定一个策略pi,进行有限次k的价值函数更新,更新还是基于贝尔曼公式,V是第i次迭代的价值
3.3 策略改进
根据截断策略评估得到的state value,对策略进行改进如果新的策略 pi_new与旧的策略 pi 相同,那么算法终止,得到策略为最优策略,否则返回继续进行迭代。
% 初始化参数
gamma = 0.9; % 折扣因子
epsilon = 1e-6; % 收敛阈值
max_iterations = 1000; % 最大迭代次数
S = 4; % 状态空间大小
A = 5; % 动作空间大小
truncation_steps = 5; % 截断策略评估的迭代次数% 转移概率矩阵 P(s'|s, a) - 4x5x4 的三维矩阵
P = zeros(S, A, S);% 奖励函数 R(s, a) - 4x5 的矩阵
R = [-1, 4, -1, -1, -1;-1, 4, -1, -1, -1;4, -1, -1, -1, -1;-1, -1, -1, -1, 1];% 转移概率矩阵
% 动作 a=1
P(:, 1, :) = [0.8, 0.1, 0.1, 0; 0.1, 0.8, 0.1, 0; 0.2, 0.2, 0.6, 0; 0, 0, 0, 1];% 动作 a=2
P(:, 2, :) = [0.6, 0.3, 0.1, 0;0.1, 0.7, 0.2, 0;0.3, 0.3, 0.4, 0;0, 0, 0, 1];% 动作 a=3
P(:, 3, :) = [0.7, 0.2, 0.1, 0;0.1, 0.8, 0.1, 0;0.2, 0.2, 0.6, 0;0, 0, 0, 1];% 动作 a=4
P(:, 4, :) = [0.5, 0.4, 0.1, 0;0.2, 0.7, 0.1, 0;0.4, 0.4, 0.2, 0;0, 0, 0, 1];% 动作 a=5
P(:, 5, :) = [0.9, 0.05, 0.05, 0;0.05, 0.9, 0.05, 0;0.1, 0.1, 0.8, 0;0, 0, 0, 1];% 初始化策略(均匀随机策略)
policy = ones(S, A) / A; % 每个状态下所有动作的概率均等% 初始化状态价值函数 V(s)
V = zeros(S, 1);% 截断策略迭代算法
for k = 1:max_iterations% 截断策略评估for step = 1:truncation_stepsV_new = zeros(S, 1);% 遍历每个状态for s = 1:S% 计算当前策略下的期望回报expected_return = 0;for a = 1:Aexpected_return = expected_return + policy(s, a) * (R(s, a) + gamma * sum(squeeze(P(s, a, :)) .* V));endV_new(s) = expected_return;end% 检查收敛性if max(abs(V_new - V)) < epsilonbreak;end% 更新状态价值V = V_new;end% 策略改进policy_stable = true; % 假设策略已经稳定for s = 1:S% 找到当前策略下的动作old_action = find(policy(s, :) == max(policy(s, :)), 1);% 计算每个动作的期望回报action_values = zeros(A, 1);for a = 1:Aaction_values(a) = R(s, a) + gamma * sum(squeeze(P(s, a, :)) .* V);end% 找到最优动作[~, best_action] = max(action_values);% 更新策略new_policy = zeros(1, A);new_policy(best_action) = 1;% 检查策略是否变化if any(policy(s, :) ~= new_policy)policy_stable = false;end% 更新策略policy(s, :) = new_policy;end% 如果策略稳定,退出循环if policy_stablebreak;end
end% 输出结果
disp('最优状态价值函数 V*(s):');
disp(V);disp('最优策略 pi*(s):');
[~, pi] = max(policy, [], 2); % 从策略矩阵中提取最优动作
disp(pi);
在代码内,定义了一个新的参数 truncation_steps
,用于指定策略评估步骤的迭代次数,策略循环中,收敛性检查依然是必须的,态价值函数 V
已经收敛(即变化小于 epsilon
),则提前退出策略评估循环。在策略改进时,直接使用贪心策略(即选择当前状态下期望回报最大的动作),并检查策略是否发生变化。如果策略没有变化,则认为策略已经稳定,退出主循环。并且在改进的时候,直接更新为最优策略,就不是一步步来了,全部算完然后从头来,直接哪个最好使用哪个,这就是上面说的截断,也是一部到位的方法。